summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/network/fetch.c14
-rw-r--r--include/git2/config.h9
-rw-r--r--include/git2/diff.h15
-rw-r--r--include/git2/errors.h1
-rw-r--r--include/git2/indexer.h2
-rw-r--r--include/git2/oid.h2
-rw-r--r--include/git2/remote.h2
-rw-r--r--include/git2/repository.h153
-rw-r--r--include/git2/submodule.h460
-rw-r--r--src/attr.c26
-rw-r--r--src/attr.h1
-rw-r--r--src/attr_file.c15
-rw-r--r--src/buffer.c37
-rw-r--r--src/checkout.c15
-rw-r--r--src/config.c32
-rw-r--r--src/config.h1
-rw-r--r--src/config_file.c12
-rw-r--r--src/config_file.h12
-rw-r--r--src/diff.c2
-rw-r--r--src/diff_output.c19
-rw-r--r--src/errors.c5
-rw-r--r--src/fetch.c69
-rw-r--r--src/filebuf.c1
-rw-r--r--src/fileops.c325
-rw-r--r--src/fileops.h78
-rw-r--r--src/ignore.c20
-rw-r--r--src/indexer.c12
-rw-r--r--src/iterator.c21
-rw-r--r--src/message.c2
-rw-r--r--src/path.c46
-rw-r--r--src/path.h14
-rw-r--r--src/pkt.c51
-rw-r--r--src/pkt.h10
-rw-r--r--src/posix.h7
-rw-r--r--src/protocol.c14
-rw-r--r--src/protocol.h4
-rw-r--r--src/remote.c8
-rw-r--r--src/repo_template.h58
-rw-r--r--src/repository.c584
-rw-r--r--src/repository.h11
-rw-r--r--src/submodule.c1491
-rw-r--r--src/submodule.h102
-rw-r--r--src/transport.h12
-rw-r--r--src/transports/git.c2
-rw-r--r--src/transports/http.c2
-rw-r--r--src/unix/posix.h2
-rw-r--r--tests-clar/checkout/checkout.c2
-rw-r--r--tests-clar/clar_libgit2.h2
-rw-r--r--tests-clar/core/buffer.c2
-rw-r--r--tests-clar/core/copy.c126
-rw-r--r--tests-clar/core/mkdir.c182
-rw-r--r--tests-clar/refs/list.c2
-rw-r--r--tests-clar/repo/init.c87
-rw-r--r--tests-clar/resources/submod2/.gitted/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/config20
-rw-r--r--tests-clar/resources/submod2/.gitted/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/indexbin0 -> 944 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/logs/HEAD4
-rw-r--r--tests-clar/resources/submod2/.gitted/logs/refs/heads/master4
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_changed_file/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_changed_head/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b42473
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f42
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_changed_index/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6bin0 -> 134 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_missing_commits/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config13
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description1
-rwxr-xr-xtests-clar/resources/submod2/.gitted/modules/sm_unchanged/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude6
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs2
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0ebin0 -> 197 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef2431
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea74
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970bin0 -> 157 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8badbin0 -> 144 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478bin0 -> 40 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398bin0 -> 136 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b2
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620dbin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9bin0 -> 173 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6bin0 -> 48 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698bin0 -> 137 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e842
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89bin0 -> 80 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9bin0 -> 165 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cbbin0 -> 110 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc82
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471cbin0 -> 246 bytes
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc238332
-rw-r--r--tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b1
-rw-r--r--tests-clar/resources/submod2/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/README.txt3
-rw-r--r--tests-clar/resources/submod2/gitmodules21
-rw-r--r--tests-clar/resources/submod2/just_a_dir/contents1
-rw-r--r--tests-clar/resources/submod2/just_a_file1
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/HEAD1
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/config6
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/description1
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/applypatch-msg.sample15
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/commit-msg.sample24
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/post-update.sample8
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-applypatch.sample14
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-commit.sample50
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-rebase.sample169
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/prepare-commit-msg.sample36
-rwxr-xr-xtests-clar/resources/submod2/not_submodule/.gitted/hooks/update.sample128
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/indexbin0 -> 112 bytes
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/logs/HEAD1
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444bin0 -> 132 bytes
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627bin0 -> 52 bytes
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195ebin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2/not_submodule/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2/not_submodule/README.txt1
-rw-r--r--tests-clar/resources/submod2/sm_added_and_uncommited/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_added_and_uncommited/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify3
-rw-r--r--tests-clar/resources/submod2/sm_changed_file/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_changed_file/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_changed_file/file_to_modify4
-rw-r--r--tests-clar/resources/submod2/sm_changed_head/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_changed_head/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_changed_head/file_to_modify4
-rw-r--r--tests-clar/resources/submod2/sm_changed_index/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_changed_index/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_changed_index/file_to_modify4
-rw-r--r--tests-clar/resources/submod2/sm_changed_untracked_file/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_changed_untracked_file/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify3
-rw-r--r--tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked1
-rw-r--r--tests-clar/resources/submod2/sm_missing_commits/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_missing_commits/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_missing_commits/file_to_modify3
-rw-r--r--tests-clar/resources/submod2/sm_unchanged/.gitted1
-rw-r--r--tests-clar/resources/submod2/sm_unchanged/README.txt3
-rw-r--r--tests-clar/resources/submod2/sm_unchanged/file_to_modify3
-rw-r--r--tests-clar/resources/submod2_target/.gitted/HEAD1
-rw-r--r--tests-clar/resources/submod2_target/.gitted/config6
-rw-r--r--tests-clar/resources/submod2_target/.gitted/description1
-rwxr-xr-xtests-clar/resources/submod2_target/.gitted/hooks/applypatch-msg.sample15
-rw-r--r--tests-clar/resources/submod2_target/.gitted/indexbin0 -> 192 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/submod2_target/.gitted/logs/HEAD4
-rw-r--r--tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master4
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/submod2_target/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/submod2_target/README.txt3
-rw-r--r--tests-clar/resources/submod2_target/file_to_modify3
-rw-r--r--tests-clar/resources/template/branches/.gitignore2
-rw-r--r--tests-clar/resources/template/description1
-rwxr-xr-xtests-clar/resources/template/hooks/applypatch-msg.sample15
-rwxr-xr-xtests-clar/resources/template/hooks/commit-msg.sample24
-rwxr-xr-xtests-clar/resources/template/hooks/post-commit.sample8
-rwxr-xr-xtests-clar/resources/template/hooks/post-receive.sample15
-rwxr-xr-xtests-clar/resources/template/hooks/post-update.sample8
-rwxr-xr-xtests-clar/resources/template/hooks/pre-applypatch.sample14
-rwxr-xr-xtests-clar/resources/template/hooks/pre-commit.sample46
-rwxr-xr-xtests-clar/resources/template/hooks/pre-rebase.sample169
-rwxr-xr-xtests-clar/resources/template/hooks/prepare-commit-msg.sample36
-rwxr-xr-xtests-clar/resources/template/hooks/update.sample128
-rw-r--r--tests-clar/resources/template/info/exclude6
-rw-r--r--tests-clar/status/ignore.c15
-rw-r--r--tests-clar/status/status_data.h8
-rw-r--r--tests-clar/status/status_helpers.h8
-rw-r--r--tests-clar/status/submodules.c24
-rw-r--r--tests-clar/status/worktree.c4
-rw-r--r--tests-clar/submodule/lookup.c110
-rw-r--r--tests-clar/submodule/modify.c257
-rw-r--r--tests-clar/submodule/status.c308
-rw-r--r--tests-clar/submodule/submodule_helpers.c84
-rw-r--r--tests-clar/submodule/submodule_helpers.h2
-rw-r--r--tests-clar/valgrind-supp-mac.txt82
347 files changed, 5953 insertions, 612 deletions
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 52e0412f..fa941b97 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -14,6 +14,13 @@ struct dl_data {
int finished;
};
+static void progress_cb(const char *str, int len, void *data)
+{
+ data = data;
+ printf("remote: %.*s", len, str);
+ fflush(stdout); /* We don't have the \n to force the flush */
+}
+
static void *download(void *ptr)
{
struct dl_data *data = (struct dl_data *)ptr;
@@ -43,6 +50,7 @@ exit:
static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
+ data = data;
git_oid_fmt(b_str, b);
b_str[GIT_OID_HEXSZ] = '\0';
@@ -78,6 +86,7 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the callbacks (only update_tips for now)
memset(&callbacks, 0, sizeof(callbacks));
callbacks.update_tips = &update_cb;
+ callbacks.progress = &progress_cb;
git_remote_set_callbacks(remote, &callbacks);
// Set up the information for the background worker thread
@@ -96,7 +105,10 @@ int fetch(git_repository *repo, int argc, char **argv)
// the download rate.
do {
usleep(10000);
- printf("\rReceived %d/%d objects in %zu bytes", stats.processed, stats.total, bytes);
+
+ if (stats.total > 0)
+ printf("Received %d/%d objects (%d) in %d bytes\r",
+ stats.received, stats.total, stats.processed, bytes);
} while (!data.finished);
if (data.ret < 0)
diff --git a/include/git2/config.h b/include/git2/config.h
index f415fbd9..21d8a0b0 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -80,15 +80,16 @@ GIT_EXTERN(int) git_config_find_global(char *global_config_path, size_t length);
GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length);
/**
- * Open the global configuration file
+ * Open the global and system configuration files
*
- * Utility wrapper that calls `git_config_find_global`
- * and opens the located file, if it exists.
+ * Utility wrapper that finds the global and system configuration files
+ * and opens them into a single prioritized config object that can be
+ * used when accessing default config data outside a repository.
*
* @param out Pointer to store the config instance
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_open_global(git_config **out);
+GIT_EXTERN(int) git_config_open_default(git_config **out);
/**
* Create a configuration file backend for ondisk files
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 79ef7a49..088e1ecf 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -391,6 +391,21 @@ GIT_EXTERN(int) git_diff_print_patch(
void *cb_data,
git_diff_data_fn print_cb);
+/**
+ * Query how many diff records are there in a diff list.
+ *
+ * You can optionally pass in a `git_delta_t` value if you want a count
+ * of just entries that match that delta type, or pass -1 for all delta
+ * records.
+ *
+ * @param diff A git_diff_list generated by one of the above functions
+ * @param delta_t A git_delta_t value to filter the count, or -1 for all records
+ * @return Count of number of deltas matching delta_t type
+ */
+GIT_EXTERN(int) git_diff_entrycount(
+ git_diff_list *diff,
+ int delta_t);
+
/**@}*/
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 2ab1da40..b55f8c30 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -54,6 +54,7 @@ typedef enum {
GITERR_TREE,
GITERR_INDEXER,
GITERR_SSL,
+ GITERR_SUBMODULE,
} git_error_t;
/**
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index d300ba01..92d1d9e3 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -19,6 +19,8 @@ GIT_BEGIN_DECL
typedef struct git_indexer_stats {
unsigned int total;
unsigned int processed;
+ unsigned int received;
+ unsigned int data_received;
} git_indexer_stats;
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 887b33e5..9e54a9f9 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -185,6 +185,8 @@ GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
/**
* Check is an oid is all zeros.
+ *
+ * @return 1 if all zeros, 0 otherwise.
*/
GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 96f460e9..a3913af5 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -287,7 +287,7 @@ typedef enum git_remote_completion_type {
* Set the calbacks to be called by the remote.
*/
struct git_remote_callbacks {
- int (*progress)(const char *str, void *data);
+ void (*progress)(const char *str, int len, void *data);
int (*completion)(git_remote_completion_type type, void *data);
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
void *data;
diff --git a/include/git2/repository.h b/include/git2/repository.h
index e727ff31..f520d543 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -83,6 +83,18 @@ GIT_EXTERN(int) git_repository_discover(
int across_fs,
const char *ceiling_dirs);
+/**
+ * Option flags for `git_repository_open_ext`.
+ *
+ * * GIT_REPOSITORY_OPEN_NO_SEARCH - Only open the repository if it can be
+ * immediately found in the start_path. Do not walk up from the
+ * start_path looking at parent directories.
+ * * GIT_REPOSITORY_OPEN_CROSS_FS - Unless this flag is set, open will not
+ * continue searching across filesystem boundaries (i.e. when `st_dev`
+ * changes from the `stat` system call). (E.g. Searching in a user's home
+ * directory "/home/user/source/" will not return "/.git/" as the found
+ * repo if "/" is a different filesystem than "/home".)
+ */
enum {
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
@@ -90,6 +102,20 @@ enum {
/**
* Find and open a repository with extended controls.
+ *
+ * @param repo_out Pointer to the repo which will be opened. This can
+ * actually be NULL if you only want to use the error code to
+ * see if a repo at this path could be opened.
+ * @param start_path Path to open as git repository. If the flags
+ * permit "searching", then this can be a path to a subdirectory
+ * inside the working directory of the repository.
+ * @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
+ * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
+ * prefixes at which the search for a containing repository should
+ * terminate.
+ * @return 0 on success, GIT_ENOTFOUND if no repository could be found,
+ * or -1 if there was a repository but open failed for some reason
+ * (such as repo corruption or system errors).
*/
GIT_EXTERN(int) git_repository_open_ext(
git_repository **repo,
@@ -118,13 +144,127 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo);
*
* @param repo_out pointer to the repo which will be created or reinitialized
* @param path the path to the repository
- * @param is_bare if true, a Git repository without a working directory is created
- * at the pointed path. If false, provided path will be considered as the working
- * directory into which the .git directory will be created.
+ * @param is_bare if true, a Git repository without a working directory is
+ * created at the pointed path. If false, provided path will be
+ * considered as the working directory into which the .git directory
+ * will be created.
*
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
+GIT_EXTERN(int) git_repository_init(
+ git_repository **repo_out,
+ const char *path,
+ unsigned is_bare);
+
+/**
+ * Option flags for `git_repository_init_ext`.
+ *
+ * These flags configure extra behaviors to `git_repository_init_ext`.
+ * In every case, the default behavior is the zero value (i.e. flag is
+ * not set). Just OR the flag values together for the `flags` parameter
+ * when initializing a new repo. Details of individual values are:
+ *
+ * * BARE - Create a bare repository with no working directory.
+ * * NO_REINIT - Return an EEXISTS error if the repo_path appears to
+ * already be an git repository.
+ * * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
+ * path for non-bare repos (if it is not already there), but
+ * passing this flag prevents that behavior.
+ * * MKDIR - Make the repo_path (and workdir_path) as needed. Init is
+ * always willing to create the ".git" directory even without this
+ * flag. This flag tells init to create the trailing component of
+ * the repo and workdir paths as needed.
+ * * MKPATH - Recursively make all components of the repo and workdir
+ * paths as necessary.
+ * * EXTERNAL_TEMPLATE - libgit2 normally uses internal templates to
+ * initialize a new repo. This flags enables external templates,
+ * looking the "template_path" from the options if set, or the
+ * `init.templatedir` global config if not, or falling back on
+ * "/usr/share/git-core/templates" if it exists.
+ */
+enum {
+ GIT_REPOSITORY_INIT_BARE = (1u << 0),
+ GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1),
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2),
+ GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
+ GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+};
+
+/**
+ * Mode options for `git_repository_init_ext`.
+ *
+ * Set the mode field of the `git_repository_init_options` structure
+ * either to the custom mode that you would like, or to one of the
+ * following modes:
+ *
+ * * SHARED_UMASK - Use permissions configured by umask - the default.
+ * * SHARED_GROUP - Use "--shared=group" behavior, chmod'ing the new repo
+ * to be group writable and "g+sx" for sticky group assignment.
+ * * SHARED_ALL - Use "--shared=all" behavior, adding world readability.
+ * * Anything else - Set to custom value.
+ */
+enum {
+ GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
+ GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
+ GIT_REPOSITORY_INIT_SHARED_ALL = 0002777,
+};
+
+/**
+ * Extended options structure for `git_repository_init_ext`.
+ *
+ * This contains extra options for `git_repository_init_ext` that enable
+ * additional initialization features. The fields are:
+ *
+ * * flags - Combination of GIT_REPOSITORY_INIT flags above.
+ * * mode - Set to one of the standard GIT_REPOSITORY_INIT_SHARED_...
+ * constants above, or to a custom value that you would like.
+ * * workdir_path - The path to the working dir or NULL for default (i.e.
+ * repo_path parent on non-bare repos). IF THIS IS RELATIVE PATH,
+ * IT WILL BE EVALUATED RELATIVE TO THE REPO_PATH. If this is not
+ * the "natural" working directory, a .git gitlink file will be
+ * created here linking to the repo_path.
+ * * description - If set, this will be used to initialize the "description"
+ * file in the repository, instead of using the template content.
+ * * template_path - When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set,
+ * this contains the path to use for the template directory. If
+ * this is NULL, the config or default directory options will be
+ * used instead.
+ * * initial_head - The name of the head to point HEAD at. If NULL, then
+ * this will be treated as "master" and the HEAD ref will be set
+ * to "refs/heads/master". If this begins with "refs/" it will be
+ * used verbatim; otherwise "refs/heads/" will be prefixed.
+ * * origin_url - If this is non-NULL, then after the rest of the
+ * repository initialization is completed, an "origin" remote
+ * will be added pointing to this URL.
+ */
+typedef struct {
+ uint32_t flags;
+ uint32_t mode;
+ const char *workdir_path;
+ const char *description;
+ const char *template_path;
+ const char *initial_head;
+ const char *origin_url;
+} git_repository_init_options;
+
+/**
+ * Create a new Git repository in the given folder with extended controls.
+ *
+ * This will initialize a new git repository (creating the repo_path
+ * if requested by flags) and working directory as needed. It will
+ * auto-detect the case sensitivity of the file system and if the
+ * file system supports file mode bits correctly.
+ *
+ * @param repo_out Pointer to the repo which will be created or reinitialized.
+ * @param repo_path The path to the repository.
+ * @param opts Pointer to git_repository_init_options struct.
+ * @return 0 or an error code on failure.
+ */
+GIT_EXTERN(int) git_repository_init_ext(
+ git_repository **repo_out,
+ const char *repo_path,
+ git_repository_init_options *opts);
/**
* Retrieve and resolve the reference pointed at by HEAD.
@@ -326,6 +466,11 @@ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index
*
* Use this function to get the contents of this file. Don't forget to
* remove the file after you create the commit.
+ *
+ * @param buffer Buffer to write data into or NULL to just read required size
+ * @param len Length of buffer in bytes
+ * @param repo Repository to read prepared message from
+ * @return Bytes written to buffer, GIT_ENOTFOUND if no message, or -1 on error
*/
GIT_EXTERN(int) git_repository_message(char *buffer, size_t len, git_repository *repo);
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index f65911a3..fe7f26cf 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -20,54 +20,153 @@
*/
GIT_BEGIN_DECL
+/**
+ * Opaque structure representing a submodule.
+ *
+ * Submodule support in libgit2 builds a list of known submodules and keeps
+ * it in the repository. The list is built from the .gitmodules file, the
+ * .git/config file, the index, and the HEAD tree. Items in the working
+ * directory that look like submodules (i.e. a git repo) but are not
+ * mentioned in those places won't be tracked.
+ */
+typedef struct git_submodule git_submodule;
+
+/**
+ * Values that could be specified for the update rule of a submodule.
+ *
+ * Use the DEFAULT value if you have altered the update value via
+ * `git_submodule_set_update()` and wish to reset to the original default.
+ */
typedef enum {
+ GIT_SUBMODULE_UPDATE_DEFAULT = -1,
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
GIT_SUBMODULE_UPDATE_REBASE = 1,
- GIT_SUBMODULE_UPDATE_MERGE = 2
+ GIT_SUBMODULE_UPDATE_MERGE = 2,
+ GIT_SUBMODULE_UPDATE_NONE = 3
} git_submodule_update_t;
+/**
+ * Values that could be specified for how closely to examine the
+ * working directory when getting submodule status.
+ *
+ * Use the DEFUALT value if you have altered the ignore value via
+ * `git_submodule_set_ignore()` and wish to reset to the original value.
+ */
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_DEFAULT = -1, /* reset to default */
+ GIT_SUBMODULE_IGNORE_NONE = 0, /* any change or untracked == dirty */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_DIRTY = 2, /* only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_ALL = 3 /* never dirty */
} git_submodule_ignore_t;
/**
- * Description of submodule
+ * Return codes for submodule status.
+ *
+ * A combination of these flags will be returned to describe the status of a
+ * submodule. Depending on the "ignore" property of the submodule, some of
+ * the flags may never be returned because they indicate changes that are
+ * supposed to be ignored.
+ *
+ * Submodule info is contained in 4 places: the HEAD tree, the index, config
+ * files (both .git/config and .gitmodules), and the working directory. Any
+ * or all of those places might be missing information about the submodule
+ * depending on what state the repo is in. We consider all four places to
+ * build the combination of status flags.
+ *
+ * There are four values that are not really status, but give basic info
+ * about what sources of submodule data are available. These will be
+ * returned even if ignore is set to "ALL".
+ *
+ * * IN_HEAD - superproject head contains submodule
+ * * IN_INDEX - superproject index contains submodule
+ * * IN_CONFIG - superproject gitmodules has submodule
+ * * IN_WD - superproject workdir has submodule
+ *
+ * The following values will be returned so long as ignore is not "ALL".
+ *
+ * * INDEX_ADDED - in index, not in head
+ * * INDEX_DELETED - in head, not in index
+ * * INDEX_MODIFIED - index and head don't match
+ * * WD_UNINITIALIZED - workdir contains empty directory
+ * * WD_ADDED - in workdir, not index
+ * * WD_DELETED - in index, not workdir
+ * * WD_MODIFIED - index and workdir head don't match
+ *
+ * The following can only be returned if ignore is "NONE" or "UNTRACKED".
+ *
+ * * WD_INDEX_MODIFIED - submodule workdir index is dirty
+ * * WD_WD_MODIFIED - submodule workdir has modified files
+ *
+ * Lastly, the following will only be returned for ignore "NONE".
+ *
+ * * WD_UNTRACKED - wd contains untracked files
+ */
+typedef enum {
+ GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
+ GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
+ GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
+ GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
+ GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
+ GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
+ GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
+ GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
+ GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
+ GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
+ GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
+ GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
+ GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
+} git_submodule_status_t;
+
+#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
+ (((S) & ~(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | \
+ GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD)) == 0)
+
+/**
+ * Lookup submodule information by name or path.
+ *
+ * Given either the submodule name or path (they are usually the same), this
+ * returns a structure describing the submodule.
*
- * 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:
+ * There are two expected error scenarios:
*
- * - `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.
+ * - The submodule is not mentioned in the HEAD, the index, and the config,
+ * but does "exist" in the working directory (i.e. there is a subdirectory
+ * that is a valid self-contained git repo). In this case, this function
+ * returns GIT_EEXISTS to indicate the the submodule exists but not in a
+ * state where a git_submodule can be instantiated.
+ * - The submodule is not mentioned in the HEAD, index, or config and the
+ * working directory doesn't contain a value git repo at that path.
+ * There may or may not be anything else at that path, but nothing that
+ * looks like a submodule. In this case, this returns GIT_ENOTFOUND.
*
- * 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.
+ * The submodule object is owned by the containing repo and will be freed
+ * when the repo is freed. The caller need not free the submodule.
+ *
+ * @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,
+ * GIT_EEXISTS if submodule exists in working directory only, -1 on
+ * other errors.
*/
-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;
+GIT_EXTERN(int) git_submodule_lookup(
+ git_submodule **submodule,
+ git_repository *repo,
+ const char *name);
/**
- * Iterate over all submodules of a repository.
+ * Iterate over all tracked submodules of a repository.
+ *
+ * See the note on `git_submodule` above. This iterates over the tracked
+ * submodules as decribed therein.
+ *
+ * If you are concerned about items in the working directory that look like
+ * submodules but are not tracked, the diff API will generate a diff record
+ * for workdir items that look like submodules but are not tracked, showing
+ * them as added in the workdir. Also, the status API will treat the entire
+ * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item.
*
* @param repo The repository
* @param callback Function to be called with the name of each submodule.
@@ -77,26 +176,297 @@ typedef struct {
*/
GIT_EXTERN(int) git_submodule_foreach(
git_repository *repo,
- int (*callback)(const char *name, void *payload),
+ int (*callback)(git_submodule *sm, const char *name, void *payload),
void *payload);
/**
- * Lookup submodule information by name or path.
+ * Set up a new git submodule for checkout.
*
- * Given either the submodule name or path (they are usually 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.
+ * This does "git submodule add" up to the fetch and checkout of the
+ * submodule contents. It preps a new submodule, creates an entry in
+ * .gitmodules and creates an empty initialized repository either at the
+ * given path in the working directory or in .git/modules with a gitlink
+ * from the working directory to the new repo.
*
- * @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
+ * To fully emulate "git submodule add" call this function, then open the
+ * submodule repo and perform the clone step as needed. Lastly, call
+ * `git_submodule_add_finalize()` to wrap up adding the new submodule and
+ * .gitmodules to the index to be ready to commit.
+ *
+ * @param submodule The newly created submodule ready to open for clone
+ * @param repo Superproject repository to contain the new submodule
+ * @param url URL for the submodules remote
+ * @param path Path at which the submodule should be created
+ * @param use_gitlink Should workdir contain a gitlink to the repo in
+ * .git/modules vs. repo directly in workdir.
+ * @return 0 on success, GIT_EEXISTS if submodule already exists,
+ * -1 on other errors.
*/
-GIT_EXTERN(int) git_submodule_lookup(
+GIT_EXTERN(int) git_submodule_add_setup(
git_submodule **submodule,
git_repository *repo,
- const char *name);
+ const char *url,
+ const char *path,
+ int use_gitlink);
+
+/**
+ * Resolve the setup of a new git submodule.
+ *
+ * This should be called on a submodule once you have called add setup
+ * and done the clone of the submodule. This adds the .gitmodules file
+ * and the newly cloned submodule to the index to be ready to be committed
+ * (but doesn't actually do the commit).
+ *
+ * @param submodule The submodule to finish adding.
+ */
+GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule);
+
+/**
+ * Add current submodule HEAD commit to index of superproject.
+ *
+ * @param submodule The submodule to add to the index
+ * @param write_index Boolean if this should immediately write the index
+ * file. If you pass this as false, you will have to get the
+ * git_index and explicitly call `git_index_write()` on it to
+ * save the change.
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_add_to_index(
+ git_submodule *submodule,
+ int write_index);
+
+/**
+ * Write submodule settings to .gitmodules file.
+ *
+ * This commits any in-memory changes to the submodule to the gitmodules
+ * file on disk. You may also be interested in `git_submodule_init()` which
+ * writes submodule info to ".git/config" (which is better for local changes
+ * to submodule settings) and/or `git_submodule_sync()` which writes
+ * settings about remotes to the actual submodule repository.
+ *
+ * @param submodule The submodule to write.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_save(git_submodule *submodule);
+
+/**
+ * Get the containing repository for a submodule.
+ *
+ * This returns a pointer to the repository that contains the submodule.
+ * This is a just a reference to the repository that was passed to the
+ * original `git_submodule_lookup()` call, so if that repository has been
+ * freed, then this may be a dangling reference.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to `git_repository`
+ */
+GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule);
+
+/**
+ * Get the name of submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule name
+ */
+GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule);
+
+/**
+ * Get the path to the submodule.
+ *
+ * The path is almost always the same as the submodule name, but the
+ * two are actually not required to match.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule path
+ */
+GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule);
+
+/**
+ * Get the URL for the submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule url
+ */
+GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
+
+/**
+ * Set the URL for the submodule.
+ *
+ * This sets the URL in memory for the submodule. This will be used for
+ * any following submodule actions while this submodule data is in memory.
+ *
+ * After calling this, you may wish to call `git_submodule_save()` to write
+ * the changes back to the ".gitmodules" file and `git_submodule_sync()` to
+ * write the changes to the checked out submodule repository.
+ *
+ * @param submodule Pointer to the submodule object
+ * @param url URL that should be used for the submodule
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_set_url(git_submodule *submodule, const char *url);
+
+/**
+ * Get the OID for the submodule in the index.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in index.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_index_oid(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current HEAD tree.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in the HEAD.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_head_oid(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current working directory.
+ *
+ * This returns the OID that corresponds to looking up 'HEAD' in the checked
+ * out submodule. If there are pending changes in the index or anything
+ * else, this won't notice that. You should call `git_submodule_status()`
+ * for a more complete picture about the state of the working directory.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not checked out.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_wd_oid(git_submodule *submodule);
+
+/**
+ * Get the ignore rule for the submodule.
+ *
+ * There are four ignore values:
+ *
+ * - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
+ * of the submodule from a clean checkout to be dirty, including the
+ * addition of untracked files. This is the default if unspecified.
+ * - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the
+ * working tree (i.e. call `git_status_foreach()` on the submodule) but
+ * UNTRACKED files will not count as making the submodule dirty.
+ * - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the
+ * submodule has moved for status. This is fast since it does not need to
+ * scan the working tree of the submodule at all.
+ * - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
+ * The working directory will be consider clean so long as there is a
+ * checked out version present.
+ */
+GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
+ git_submodule *submodule);
+
+/**
+ * Set the ignore rule for the submodule.
+ *
+ * This sets the ignore rule in memory for the submodule. This will be used
+ * for any following actions (such as `git_submodule_status()`) while the
+ * submodule is in memory. You should call `git_submodule_save()` if you
+ * want to persist the new ignore role.
+ *
+ * Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling
+ * `git_submodule_reload()` will revert the rule to the value that was in the
+ * original config.
+ *
+ * @return old value for ignore
+ */
+GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
+ git_submodule *submodule,
+ git_submodule_ignore_t ignore);
+
+/**
+ * Get the update rule for the submodule.
+ */
+GIT_EXTERN(git_submodule_update_t) git_submodule_update(
+ git_submodule *submodule);
+
+/**
+ * Set the update rule for the submodule.
+ *
+ * This sets the update rule in memory for the submodule. You should call
+ * `git_submodule_save()` if you want to persist the new update rule.
+ *
+ * Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling
+ * `git_submodule_reload()` will revert the rule to the value that was in the
+ * original config.
+ *
+ * @return old value for update
+ */
+GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
+ git_submodule *submodule,
+ git_submodule_update_t update);
+
+/**
+ * Copy submodule info into ".git/config" file.
+ *
+ * Just like "git submodule init", this copies information about the
+ * submodule into ".git/config". You can use the accessor functions
+ * above to alter the in-memory git_submodule object and control what
+ * is written to the config, overriding what is in .gitmodules.
+ *
+ * @param submodule The submodule to write into the superproject config
+ * @param overwrite By default, existing entries will not be overwritten,
+ * but setting this to true forces them to be updated.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
+
+/**
+ * Copy submodule remote info into submodule repo.
+ *
+ * This copies the information about the submodules URL into the checked out
+ * submodule config, acting like "git submodule sync". This is useful if
+ * you have altered the URL for the submodule (or it has been altered by a
+ * fetch of upstream changes) and you need to update your local repo.
+ */
+GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule);
+
+/**
+ * Open the repository for a submodule.
+ *
+ * This is a newly opened repository object. The caller is responsible for
+ * calling `git_repository_free()` on it when done. Multiple calls to this
+ * function will return distinct `git_repository` objects. This will only
+ * work if the submodule is checked out into the working directory.
+ *
+ * @param subrepo Pointer to the submodule repo which was opened
+ * @param submodule Submodule to be opened
+ * @return 0 on success, <0 if submodule repo could not be opened.
+ */
+GIT_EXTERN(int) git_submodule_open(
+ git_repository **repo,
+ git_submodule *submodule);
+
+/**
+ * Reread submodule info from config, index, and HEAD.
+ *
+ * Call this to reread cached submodule information for this submodule if
+ * you have reason to believe that it has changed.
+ */
+GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule);
+
+/**
+ * Reread all submodule info.
+ *
+ * Call this to reload all cached submodule information for the repo.
+ */
+GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo);
+
+/**
+ * Get the status for a submodule.
+ *
+ * This looks at a submodule and tries to determine the status. It
+ * will return a combination of the `GIT_SUBMODULE_STATUS` values above.
+ * How deeply it examines the working directory to do this will depend
+ * on the `git_submodule_ignore_t` value for the submodule - which can be
+ * set either temporarily or permanently with `git_submodule_set_ignore()`.
+ *
+ * @param status Combination of `GIT_SUBMODULE_STATUS` flags
+ * @param submodule Submodule for which to get status
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_status(
+ unsigned int *status,
+ git_submodule *submodule);
/** @} */
GIT_END_DECL
diff --git a/src/attr.c b/src/attr.c
index 8a7ff28c..99322066 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -590,6 +590,18 @@ static int collect_attr_files(
return error;
}
+static char *try_global_default(const char *relpath)
+{
+ git_buf dflt = GIT_BUF_INIT;
+ char *rval = NULL;
+
+ if (!git_futils_find_global_file(&dflt, relpath))
+ rval = git_buf_detach(&dflt);
+
+ git_buf_free(&dflt);
+
+ return rval;
+}
int git_attr_cache__init(git_repository *repo)
{
@@ -607,20 +619,14 @@ int git_attr_cache__init(git_repository *repo)
ret = git_config_get_string(&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG);
if (ret < 0 && ret != GIT_ENOTFOUND)
return ret;
+ if (ret == GIT_ENOTFOUND)
+ cache->cfg_attr_file = try_global_default(GIT_ATTR_CONFIG_DEFAULT);
ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);
if (ret < 0 && ret != GIT_ENOTFOUND)
return ret;
-
- if (ret == GIT_ENOTFOUND) {
- git_buf dflt = GIT_BUF_INIT;
-
- ret = git_futils_find_global_file(&dflt, GIT_IGNORE_CONFIG_DEFAULT);
- if (!ret)
- cache->cfg_excl_file = git_buf_detach(&dflt);
-
- git_buf_free(&dflt);
- }
+ if (ret == GIT_ENOTFOUND)
+ cache->cfg_excl_file = try_global_default(GIT_IGNORE_CONFIG_DEFAULT);
giterr_clear();
diff --git a/src/attr.h b/src/attr.h
index 78cfb57c..7589bb10 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -11,6 +11,7 @@
#include "strmap.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
+#define GIT_ATTR_CONFIG_DEFAULT ".config/git/attributes"
#define GIT_IGNORE_CONFIG "core.excludesfile"
#define GIT_IGNORE_CONFIG_DEFAULT ".config/git/ignore"
diff --git a/src/attr_file.c b/src/attr_file.c
index 20b3cf63..b2f312e3 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -250,18 +250,15 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
int git_attr_path__init(
git_attr_path *info, const char *path, const char *base)
{
+ ssize_t root;
+
/* 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;
- }
+ if (git_path_join_unrooted(&info->full, path, base, &root) < 0)
+ return -1;
+
+ info->path = info->full.ptr + root;
/* remove trailing slashes */
while (info->full.size > 0) {
diff --git a/src/buffer.c b/src/buffer.c
index b57998e1..61cfaf9e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -144,31 +144,40 @@ int git_buf_puts(git_buf *buf, const char *string)
int git_buf_puts_escaped(
git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
{
- const char *scan = string;
- size_t total = 0, esc_with_len = strlen(esc_with);
+ const char *scan;
+ size_t total = 0, esc_len = strlen(esc_with), count;
- while (*scan) {
- size_t count = strcspn(scan, esc_chars);
- total += count + 1 + esc_with_len;
- scan += count + 1;
+ if (!string)
+ return 0;
+
+ for (scan = string; *scan; ) {
+ /* count run of non-escaped characters */
+ count = strcspn(scan, esc_chars);
+ total += count;
+ scan += count;
+ /* count run of escaped characters */
+ count = strspn(scan, esc_chars);
+ total += count * (esc_len + 1);
+ scan += count;
}
ENSURE_SIZE(buf, buf->size + total + 1);
for (scan = string; *scan; ) {
- size_t count = strcspn(scan, esc_chars);
+ count = strcspn(scan, esc_chars);
memmove(buf->ptr + buf->size, scan, count);
scan += count;
buf->size += count;
- if (*scan) {
- memmove(buf->ptr + buf->size, esc_with, esc_with_len);
- buf->size += esc_with_len;
-
- memmove(buf->ptr + buf->size, scan, 1);
- scan += 1;
- buf->size += 1;
+ for (count = strspn(scan, esc_chars); count > 0; --count) {
+ /* copy escape sequence */
+ memmove(buf->ptr + buf->size, esc_with, esc_len);
+ buf->size += esc_len;
+ /* copy character to be escaped */
+ buf->ptr[buf->size] = *scan;
+ buf->size++;
+ scan++;
}
}
diff --git a/src/checkout.c b/src/checkout.c
index ac540391..88df2128 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -31,7 +31,7 @@ typedef struct tree_walk_data
git_checkout_opts *opts;
git_repository *repo;
git_odb *odb;
- bool do_symlinks;
+ bool no_symlinks;
} tree_walk_data;
@@ -48,9 +48,9 @@ static int blob_contents_to_link(tree_walk_data *data, git_buf *fnbuf,
/* Create the link */
const char *new = git_buf_cstr(&linktarget),
*old = git_buf_cstr(fnbuf);
- retcode = data->do_symlinks
- ? p_symlink(new, old)
- : git_futils_fake_symlink(new, old);
+ retcode = data->no_symlinks
+ ? git_futils_fake_symlink(new, old)
+ : p_symlink(new, old);
}
git_buf_free(&linktarget);
git_blob_free(blob);
@@ -176,13 +176,14 @@ int git_checkout_head(git_repository *repo, git_checkout_opts *opts, git_indexer
return GIT_ERROR;
}
+ memset(&payload, 0, sizeof(payload));
+
/* Determine if symlinks should be handled */
- if (!git_repository_config(&cfg, repo)) {
+ if (!git_repository_config__weakptr(&cfg, repo)) {
int temp = true;
if (!git_config_get_bool(&temp, cfg, "core.symlinks")) {
- payload.do_symlinks = !!temp;
+ payload.no_symlinks = !temp;
}
- git_config_free(cfg);
}
stats->total = stats->processed = 0;
diff --git a/src/config.c b/src/config.c
index 44cfe760..e62dccf5 100644
--- a/src/config.c
+++ b/src/config.c
@@ -449,7 +449,12 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
int git_config_find_global_r(git_buf *path)
{
- return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+ int error = git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+
+ if (error == GIT_ENOTFOUND)
+ error = git_futils_find_global_file(path, GIT_CONFIG_FILENAME_ALT);
+
+ return error;
}
int git_config_find_global(char *global_config_path, size_t length)
@@ -501,17 +506,28 @@ int git_config_find_system(char *system_config_path, size_t length)
return 0;
}
-int git_config_open_global(git_config **out)
+int git_config_open_default(git_config **out)
{
int error;
- git_buf path = GIT_BUF_INIT;
+ git_config *cfg = NULL;
+ git_buf buf = GIT_BUF_INIT;
- if ((error = git_config_find_global_r(&path)) < 0)
- return error;
+ error = git_config_new(&cfg);
- error = git_config_open_ondisk(out, git_buf_cstr(&path));
- git_buf_free(&path);
+ if (!error && !git_config_find_global_r(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr, 2);
+
+ if (!error && !git_config_find_system_r(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr, 1);
+
+ git_buf_free(&buf);
+
+ if (error && cfg) {
+ git_config_free(cfg);
+ cfg = NULL;
+ }
+
+ *out = cfg;
return error;
}
-
diff --git a/src/config.h b/src/config.h
index 82e98ce5..5475ef38 100644
--- a/src/config.h
+++ b/src/config.h
@@ -13,6 +13,7 @@
#include "repository.h"
#define GIT_CONFIG_FILENAME ".gitconfig"
+#define GIT_CONFIG_FILENAME_ALT ".config/git/config"
#define GIT_CONFIG_FILENAME_INREPO "config"
#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
#define GIT_CONFIG_FILE_MODE 0666
diff --git a/src/config_file.c b/src/config_file.c
index 547509b9..d3fb56aa 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -195,7 +195,7 @@ static int file_foreach(
void *data)
{
diskfile_backend *b = (diskfile_backend *)backend;
- cvar_t *var;
+ cvar_t *var, *next_var;
const char *key;
regex_t regex;
int result = 0;
@@ -212,7 +212,9 @@ static int file_foreach(
}
git_strmap_foreach(b->values, key, var,
- for (; var != NULL; var = CVAR_LIST_NEXT(var)) {
+ for (; var != NULL; var = next_var) {
+ next_var = CVAR_LIST_NEXT(var);
+
/* skip non-matching keys if regexp was provided */
if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
continue;
@@ -253,11 +255,17 @@ static int config_set(git_config_file *cfg, const char *name, const char *value)
char *tmp = NULL;
git__free(key);
+
if (existing->next != NULL) {
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
return -1;
}
+ /* don't update if old and new values already match */
+ if ((!existing->value && !value) ||
+ (existing->value && value && !strcmp(existing->value, value)))
+ return 0;
+
if (value) {
tmp = git__strdup(value);
GITERR_CHECK_ALLOC(tmp);
diff --git a/src/config_file.h b/src/config_file.h
index c3129288..bf687b51 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -19,12 +19,24 @@ GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
cfg->free(cfg);
}
+GIT_INLINE(int) git_config_file_get_string(
+ const char **out, git_config_file *cfg, const char *name)
+{
+ return cfg->get(cfg, name, out);
+}
+
GIT_INLINE(int) git_config_file_set_string(
git_config_file *cfg, const char *name, const char *value)
{
return cfg->set(cfg, name, value);
}
+GIT_INLINE(int) git_config_file_delete(
+ git_config_file *cfg, const char *name)
+{
+ return cfg->del(cfg, name);
+}
+
GIT_INLINE(int) git_config_file_foreach(
git_config_file *cfg,
int (*fn)(const char *key, const char *value, void *data),
diff --git a/src/diff.c b/src/diff.c
index 9abf8b9f..430f52e0 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -530,7 +530,7 @@ static int maybe_modified(
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)
+ else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
status = GIT_DELTA_UNMODIFIED;
else {
/* TODO: support other GIT_SUBMODULE_IGNORE values */
diff --git a/src/diff_output.c b/src/diff_output.c
index d269a4ce..2bf939f3 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -718,6 +718,25 @@ int git_diff_print_patch(
return error;
}
+int git_diff_entrycount(git_diff_list *diff, int delta_t)
+{
+ int count = 0;
+ unsigned int i;
+ git_diff_delta *delta;
+
+ assert(diff);
+
+ if (delta_t < 0)
+ return diff->deltas.length;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ if (delta->status == (git_delta_t)delta_t)
+ count++;
+ }
+
+ return count;
+}
+
int git_diff_blobs(
git_blob *old_blob,
git_blob *new_blob,
diff --git a/src/errors.c b/src/errors.c
index d43d7d9b..802ad364 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -110,6 +110,11 @@ void giterr_set_regex(const regex_t *regex, int error_code)
void giterr_clear(void)
{
GIT_GLOBAL->last_error = NULL;
+
+ errno = 0;
+#ifdef GIT_WIN32
+ SetLastError(0);
+#endif
}
const git_error *giterr_last(void)
diff --git a/src/fetch.c b/src/fetch.c
index d96ac778..4c7e8254 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -292,6 +292,31 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
}
+static int no_sideband(git_indexer_stream *idx, gitno_buffer *buf, git_off_t *bytes, git_indexer_stats *stats)
+{
+ int recvd;
+
+ do {
+ if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0)
+ return -1;
+
+ gitno_consume_n(buf, buf->offset);
+
+ if ((recvd = gitno_recv(buf)) < 0)
+ return -1;
+
+ *bytes += recvd;
+ } while(recvd > 0 && stats->data_received);
+
+ if (!stats->data_received)
+ giterr_set(GITERR_NET, "Early EOF while downloading packfile");
+
+ if (git_indexer_stream_finalize(idx, stats))
+ return -1;
+
+ return 0;
+}
+
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(
git_transport *t,
@@ -299,7 +324,6 @@ int git_fetch__download_pack(
git_off_t *bytes,
git_indexer_stats *stats)
{
- int recvd;
git_buf path = GIT_BUF_INIT;
gitno_buffer *buf = &t->buffer;
git_indexer_stream *idx = NULL;
@@ -314,20 +338,49 @@ int git_fetch__download_pack(
memset(stats, 0, sizeof(git_indexer_stats));
*bytes = 0;
- do {
- if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0)
+ /*
+ * If the remote doesn't support the side-band, we can feed
+ * the data directly to the indexer. Otherwise, we need to
+ * check which one belongs there.
+ */
+ if (!t->caps.side_band && !t->caps.side_band_64k) {
+ if (no_sideband(idx, buf, bytes, stats) < 0)
goto on_error;
- gitno_consume_n(buf, buf->offset);
+ git_indexer_stream_free(idx);
+ return 0;
+ }
- if ((recvd = gitno_recv(buf)) < 0)
+ do {
+ git_pkt *pkt;
+ if (recv_pkt(&pkt, buf) < 0)
goto on_error;
- *bytes += recvd;
- } while(recvd > 0);
+ if (pkt->type == GIT_PKT_PROGRESS) {
+ if (t->progress_cb) {
+ git_pkt_progress *p = (git_pkt_progress *) pkt;
+ t->progress_cb(p->data, p->len, t->cb_data);
+ }
+ git__free(pkt);
+ } else if (pkt->type == GIT_PKT_DATA) {
+ git_pkt_data *p = (git_pkt_data *) pkt;
+ *bytes += p->len;
+ if (git_indexer_stream_add(idx, p->data, p->len, stats) < 0)
+ goto on_error;
+
+ git__free(pkt);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* A flush indicates the end of the packfile */
+ git__free(pkt);
+ break;
+ }
+ } while (!stats->data_received);
+
+ if (!stats->data_received)
+ giterr_set(GITERR_NET, "Early EOF while downloading packfile");
if (git_indexer_stream_finalize(idx, stats))
- goto on_error;
+ return -1;
git_indexer_stream_free(idx);
return 0;
diff --git a/src/filebuf.c b/src/filebuf.c
index 8b3ebb3e..cfc8528e 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -50,6 +50,7 @@ static int lock_file(git_filebuf *file, int flags)
if (flags & GIT_FILEBUF_FORCE)
p_unlink(file->path_lock);
else {
+ giterr_clear(); /* actual OS error code just confuses */
giterr_set(GITERR_OS,
"Failed to lock file '%s' for writing", file->path_lock);
return -1;
diff --git a/src/fileops.c b/src/fileops.c
index 4de58b0c..76ef8c91 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -10,19 +10,8 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
- int result = 0;
- git_buf target_folder = GIT_BUF_INIT;
-
- if (git_path_dirname_r(&target_folder, file_path) < 0)
- return -1;
-
- /* Does the containing folder exist? */
- if (git_path_isdir(target_folder.ptr) == false)
- /* Let's create the tree structure */
- result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
-
- git_buf_free(&target_folder);
- return result;
+ return git_futils_mkdir(
+ file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
}
int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -239,66 +228,92 @@ void git_futils_mmap_free(git_map *out)
p_munmap(out);
}
-int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
+int git_futils_mkdir(
+ const char *path,
+ const char *base,
+ mode_t mode,
+ uint32_t flags)
{
git_buf make_path = GIT_BUF_INIT;
- size_t start = 0;
- char *pp, *sp;
- bool failed = false;
-
- if (base != NULL) {
- /*
- * when a base is being provided, it is supposed to already exist.
- * Therefore, no attempt is being made to recursively create this leading path
- * segment. It's just skipped. */
- start = strlen(base);
- if (git_buf_joinpath(&make_path, base, path) < 0)
- return -1;
- } else {
- int root_path_offset;
+ ssize_t root = 0;
+ char lastch, *tail;
- if (git_buf_puts(&make_path, path) < 0)
- return -1;
+ /* build path and find "root" where we should start calling mkdir */
+ if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
+ return -1;
- root_path_offset = git_path_root(make_path.ptr);
- if (root_path_offset > 0) {
- /*
- * On Windows, will skip the drive name (eg. C: or D:)
- * or the leading part of a network path (eg. //computer_name ) */
- start = root_path_offset;
- }
+ if (make_path.size == 0) {
+ giterr_set(GITERR_OS, "Attempt to create empty path");
+ goto fail;
}
- pp = make_path.ptr + start;
-
- while (!failed && (sp = strchr(pp, '/')) != NULL) {
- if (sp != pp && git_path_isdir(make_path.ptr) == false) {
- *sp = 0;
-
- /* Do not choke while trying to recreate an existing directory */
- if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
- failed = true;
+ /* remove trailing slashes on path */
+ while (make_path.ptr[make_path.size - 1] == '/') {
+ make_path.size--;
+ make_path.ptr[make_path.size] = '\0';
+ }
- *sp = '/';
+ /* if we are not supposed to made the last element, truncate it */
+ if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
+ git_buf_rtruncate_at_char(&make_path, '/');
+
+ /* if we are not supposed to make the whole path, reset root */
+ if ((flags & GIT_MKDIR_PATH) == 0)
+ root = git_buf_rfind(&make_path, '/');
+
+ /* clip root to make_path length */
+ if (root >= (ssize_t)make_path.size)
+ root = (ssize_t)make_path.size - 1;
+ if (root < 0)
+ root = 0;
+
+ tail = & make_path.ptr[root];
+
+ while (*tail) {
+ /* advance tail to include next path component */
+ while (*tail == '/')
+ tail++;
+ while (*tail && *tail != '/')
+ tail++;
+
+ /* truncate path at next component */
+ lastch = *tail;
+ *tail = '\0';
+
+ /* make directory */
+ if (p_mkdir(make_path.ptr, mode) < 0 &&
+ (errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
+ {
+ giterr_set(GITERR_OS, "Failed to make directory '%s'",
+ make_path.ptr);
+ goto fail;
}
- pp = sp + 1;
- }
+ /* chmod if requested */
+ if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
+ ((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
+ {
+ if (p_chmod(make_path.ptr, mode) < 0) {
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
+ make_path.ptr);
+ goto fail;
+ }
+ }
- if (*pp != '\0' && !failed) {
- if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
- failed = true;
+ *tail = lastch;
}
git_buf_free(&make_path);
+ return 0;
- if (failed) {
- giterr_set(GITERR_OS,
- "Failed to create directory structure at '%s'", path);
- return -1;
- }
+fail:
+ git_buf_free(&make_path);
+ return -1;
+}
- return 0;
+int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
+{
+ return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
}
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
@@ -495,3 +510,197 @@ int git_futils_fake_symlink(const char *old, const char *new)
}
return retcode;
}
+
+static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
+{
+ int error = 0;
+ char buffer[4096];
+ ssize_t len = 0;
+
+ while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
+ /* p_write() does not have the same semantics as write(). It loops
+ * internally and will return 0 when it has completed writing.
+ */
+ error = p_write(ofd, buffer, len);
+
+ if (len < 0) {
+ giterr_set(GITERR_OS, "Read error while copying file");
+ error = (int)len;
+ }
+
+ if (close_fd_when_done) {
+ p_close(ifd);
+ p_close(ofd);
+ }
+
+ return error;
+}
+
+int git_futils_cp(const char *from, const char *to, mode_t filemode)
+{
+ int ifd, ofd;
+
+ if ((ifd = git_futils_open_ro(from)) < 0)
+ return ifd;
+
+ if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ ofd = GIT_ENOTFOUND;
+ giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
+ p_close(ifd);
+ return ofd;
+ }
+
+ return cp_by_fd(ifd, ofd, true);
+}
+
+static int cp_link(const char *from, const char *to, size_t link_size)
+{
+ int error = 0;
+ ssize_t read_len;
+ char *link_data = git__malloc(link_size + 1);
+ GITERR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(from, link_data, link_size);
+ if (read_len != (ssize_t)link_size) {
+ giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
+ error = -1;
+ }
+ else {
+ link_data[read_len] = '\0';
+
+ if (p_symlink(link_data, to) < 0) {
+ giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
+ link_data, to);
+ error = -1;
+ }
+ }
+
+ git__free(link_data);
+ return error;
+}
+
+typedef struct {
+ const char *to_root;
+ git_buf to;
+ ssize_t from_prefix;
+ uint32_t flags;
+ uint32_t mkdir_flags;
+ mode_t dirmode;
+} cp_r_info;
+
+static int _cp_r_callback(void *ref, git_buf *from)
+{
+ cp_r_info *info = ref;
+ struct stat from_st, to_st;
+ bool exists = false;
+
+ if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
+ from->ptr[git_path_basename_offset(from)] == '.')
+ return 0;
+
+ if (git_buf_joinpath(
+ &info->to, info->to_root, from->ptr + info->from_prefix) < 0)
+ return -1;
+
+ if (p_lstat(info->to.ptr, &to_st) < 0) {
+ if (errno != ENOENT && errno != ENOTDIR) {
+ giterr_set(GITERR_OS,
+ "Could not access %s while copying files", info->to.ptr);
+ return -1;
+ }
+ } else
+ exists = true;
+
+ if (git_path_lstat(from->ptr, &from_st) < 0)
+ return -1;
+
+ if (S_ISDIR(from_st.st_mode)) {
+ int error = 0;
+ mode_t oldmode = info->dirmode;
+
+ /* if we are not chmod'ing, then overwrite dirmode */
+ if ((info->flags & GIT_CPDIR_CHMOD) == 0)
+ info->dirmode = from_st.st_mode;
+
+ /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
+ if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
+ error = git_futils_mkdir(
+ info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
+
+ /* recurse onto target directory */
+ if (!exists || S_ISDIR(to_st.st_mode))
+ error = git_path_direach(from, _cp_r_callback, info);
+
+ if (oldmode != 0)
+ info->dirmode = oldmode;
+
+ return error;
+ }
+
+ if (exists) {
+ if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
+ return 0;
+
+ if (p_unlink(info->to.ptr) < 0) {
+ giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
+ info->to.ptr);
+ return -1;
+ }
+ }
+
+ /* Done if this isn't a regular file or a symlink */
+ if (!S_ISREG(from_st.st_mode) &&
+ (!S_ISLNK(from_st.st_mode) ||
+ (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
+ return 0;
+
+ /* Make container directory on demand if needed */
+ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
+ git_futils_mkdir(
+ info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
+ return -1;
+
+ /* make symlink or regular file */
+ if (S_ISLNK(from_st.st_mode))
+ return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
+ else
+ return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
+}
+
+int git_futils_cp_r(
+ const char *from,
+ const char *to,
+ uint32_t flags,
+ mode_t dirmode)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT;
+ cp_r_info info;
+
+ if (git_buf_sets(&path, from) < 0)
+ return -1;
+
+ info.to_root = to;
+ info.flags = flags;
+ info.dirmode = dirmode;
+ info.from_prefix = path.size;
+ git_buf_init(&info.to, 0);
+
+ /* precalculate mkdir flags */
+ if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
+ info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
+ if ((flags & GIT_CPDIR_CHMOD) != 0)
+ info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
+ } else {
+ info.mkdir_flags =
+ ((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
+ }
+
+ error = _cp_r_callback(&info, &path);
+
+ git_buf_free(&path);
+ git_buf_free(&info.to);
+
+ return error;
+}
diff --git a/src/fileops.h b/src/fileops.h
index 594eacbd..5c23ce30 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -50,12 +50,47 @@ extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmo
/**
* Create a path recursively
*
- * If a base parameter is being passed, it's expected to be valued with a path pointing to an already
- * exisiting directory.
+ * If a base parameter is being passed, it's expected to be valued with a
+ * path pointing to an already existing directory.
*/
extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
/**
+ * Flags to pass to `git_futils_mkdir`.
+ *
+ * * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists.
+ * * GIT_MKDIR_PATH says to make all components in the path.
+ * * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
+ * * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
+ * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
+ *
+ * Note that the chmod options will be executed even if the directory already
+ * exists, unless GIT_MKDIR_EXCL is given.
+ */
+typedef enum {
+ GIT_MKDIR_EXCL = 1,
+ GIT_MKDIR_PATH = 2,
+ GIT_MKDIR_CHMOD = 4,
+ GIT_MKDIR_CHMOD_PATH = 8,
+ GIT_MKDIR_SKIP_LAST = 16
+} git_futils_mkdir_flags;
+
+/**
+ * Create a directory or entire path.
+ *
+ * This makes a directory (and the entire path leading up to it if requested),
+ * and optionally chmods the directory immediately after (or each part of the
+ * path if requested).
+ *
+ * @param path The path to create.
+ * @param base Root for relative path. These directories will never be made.
+ * @param mode The mode to use for created directories.
+ * @param flags Combination of the mkdir flags above.
+ * @return 0 on success, else error code
+ */
+extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
+
+/**
* Create all the folders required to contain
* the full path of a file
*/
@@ -95,6 +130,45 @@ 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);
/**
+ * Copy a file
+ *
+ * The filemode will be used for the newly created file.
+ */
+extern int git_futils_cp(
+ const char *from,
+ const char *to,
+ mode_t filemode);
+
+/**
+ * Flags that can be passed to `git_futils_cp_r`.
+ */
+typedef enum {
+ GIT_CPDIR_CREATE_EMPTY_DIRS = 1,
+ GIT_CPDIR_COPY_SYMLINKS = 2,
+ GIT_CPDIR_COPY_DOTFILES = 4,
+ GIT_CPDIR_OVERWRITE = 8,
+ GIT_CPDIR_CHMOD = 16
+} git_futils_cpdir_flags;
+
+/**
+ * Copy a directory tree.
+ *
+ * This copies directories and files from one root to another. You can
+ * pass a combinationof GIT_CPDIR flags as defined above.
+ *
+ * If you pass the CHMOD flag, then the dirmode will be applied to all
+ * directories that are created during the copy, overiding the natural
+ * permissions. If you do not pass the CHMOD flag, then the dirmode
+ * will actually be copied from the source files and the `dirmode` arg
+ * will be ignored.
+ */
+extern int git_futils_cp_r(
+ const char *from,
+ const char *to,
+ uint32_t flags,
+ mode_t dirmode);
+
+/**
* Open a file readonly and set error if needed.
*/
extern int git_futils_open_ro(const char *path);
diff --git a/src/ignore.c b/src/ignore.c
index 1ac8afdf..3c2f19ab 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -205,6 +205,16 @@ cleanup:
return 0;
}
+static int get_internal_ignores(git_attr_file **ign, git_repository *repo)
+{
+ int error;
+
+ if (!(error = git_attr_cache__init(repo)))
+ error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign);
+
+ return error;
+}
+
int git_ignore_add_rule(
git_repository *repo,
const char *rules)
@@ -212,10 +222,7 @@ int git_ignore_add_rule(
int error;
git_attr_file *ign_internal;
- error = git_attr_cache__internal_file(
- repo, GIT_IGNORE_INTERNAL, &ign_internal);
-
- if (!error && ign_internal != NULL)
+ if (!(error = get_internal_ignores(&ign_internal, repo)))
error = parse_ignore_file(repo, rules, ign_internal);
return error;
@@ -227,10 +234,7 @@ int git_ignore_clear_internal_rules(
int error;
git_attr_file *ign_internal;
- error = git_attr_cache__internal_file(
- repo, GIT_IGNORE_INTERNAL, &ign_internal);
-
- if (!error && ign_internal != NULL)
+ if (!(error = get_internal_ignores(&ign_internal, repo)))
git_attr_file__clear_rules(ign_internal);
return error;
diff --git a/src/indexer.c b/src/indexer.c
index 797a5827..30c6469a 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -324,8 +324,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
return -1;
+ memset(stats, 0, sizeof(git_indexer_stats));
stats->total = (unsigned int)idx->nr_objects;
- stats->processed = 0;
}
/* Now that we have data in the pack, let's try to parse it */
@@ -361,6 +361,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (error < 0)
return error;
+ stats->received++;
continue;
}
@@ -379,8 +380,17 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
git__free(obj.data);
stats->processed = (unsigned int)++processed;
+ stats->received++;
}
+ /*
+ * If we've received all of the objects and our packfile is
+ * one hash beyond the end of the last object, all of the
+ * packfile is here.
+ */
+ if (stats->received == idx->nr_objects && idx->pack->mwf.size >= idx->off + 20)
+ stats->data_received = 1;
+
return 0;
on_error:
diff --git a/src/iterator.c b/src/iterator.c
index 819b0e22..92fe6713 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -525,7 +525,9 @@ static int workdir_iterator__advance(
while ((wf = wi->stack) != NULL) {
next = git_vector_get(&wf->entries, ++wf->index);
if (next != NULL) {
- if (strcmp(next->path, DOT_GIT "/") == 0)
+ /* match git's behavior of ignoring anything named ".git" */
+ if (strcmp(next->path, DOT_GIT "/") == 0 ||
+ strcmp(next->path, DOT_GIT) == 0)
continue;
/* else found a good entry */
break;
@@ -607,8 +609,8 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
wi->entry.path = ps->path;
- /* skip over .git directory */
- if (strcmp(ps->path, DOT_GIT "/") == 0)
+ /* skip over .git entry */
+ if (strcmp(ps->path, DOT_GIT "/") == 0 || 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 */
@@ -629,15 +631,10 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
/* 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();
- }
+ int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
+ bool is_submodule = (res == 0);
+ if (res == GIT_ENOTFOUND)
+ giterr_clear();
/* if submodule, mark as GITLINK and remove trailing slash */
if (is_submodule) {
diff --git a/src/message.c b/src/message.c
index e6dedc9f..791b6945 100644
--- a/src/message.c
+++ b/src/message.c
@@ -82,5 +82,5 @@ int git_message_prettify(char *message_out, size_t buffer_size, const char *mess
done:
git_buf_free(&buf);
- return out_size;
+ return (int)out_size;
}
diff --git a/src/path.c b/src/path.c
index 22391c52..15188850 100644
--- a/src/path.c
+++ b/src/path.c
@@ -147,6 +147,20 @@ char *git_path_basename(const char *path)
return basename;
}
+size_t git_path_basename_offset(git_buf *buffer)
+{
+ ssize_t slash;
+
+ if (!buffer || buffer->size <= 0)
+ return 0;
+
+ slash = git_buf_rfind_next(buffer, '/');
+
+ if (slash >= 0 && buffer->ptr[slash] == '/')
+ return (size_t)(slash + 1);
+
+ return 0;
+}
const char *git_path_topdir(const char *path)
{
@@ -193,6 +207,31 @@ int git_path_root(const char *path)
return -1; /* Not a real error - signals that path is not rooted */
}
+int git_path_join_unrooted(
+ git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
+{
+ int error, root;
+
+ assert(path && path_out);
+
+ root = git_path_root(path);
+
+ if (base != NULL && root < 0) {
+ error = git_buf_joinpath(path_out, base, path);
+
+ if (root_at)
+ *root_at = (ssize_t)strlen(base);
+ }
+ else {
+ error = git_buf_sets(path_out, path);
+
+ if (root_at)
+ *root_at = (root < 0) ? 0 : (ssize_t)root;
+ }
+
+ return error;
+}
+
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{
char buf[GIT_PATH_MAX];
@@ -502,12 +541,7 @@ bool git_path_contains_file(git_buf *base, const char *file)
int git_path_find_dir(git_buf *dir, const char *path, const char *base)
{
- int error;
-
- if (base != NULL && git_path_root(path) < 0)
- error = git_buf_joinpath(dir, base, path);
- else
- error = git_buf_sets(dir, path);
+ int error = git_path_join_unrooted(dir, path, base, NULL);
if (!error) {
char buf[GIT_PATH_MAX];
diff --git a/src/path.h b/src/path.h
index 14618b2f..b6292277 100644
--- a/src/path.h
+++ b/src/path.h
@@ -58,6 +58,11 @@ extern int git_path_dirname_r(git_buf *buffer, const char *path);
extern char *git_path_basename(const char *path);
extern int git_path_basename_r(git_buf *buffer, const char *path);
+/* Return the offset of the start of the basename. Unlike the other
+ * basename functions, this returns 0 if the path is empty.
+ */
+extern size_t git_path_basename_offset(git_buf *buffer);
+
extern const char *git_path_topdir(const char *path);
/**
@@ -186,6 +191,15 @@ extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
extern bool git_path_contains_file(git_buf *dir, const char *file);
/**
+ * Prepend base to unrooted path or just copy path over.
+ *
+ * This will optionally return the index into the path where the "root"
+ * is, either the end of the base directory prefix or the path root.
+ */
+extern int git_path_join_unrooted(
+ git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
+
+/**
* Clean up path, prepending base if it is not already rooted.
*/
extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
diff --git a/src/pkt.c b/src/pkt.c
index 8c916fff..ad0149d3 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -17,6 +17,7 @@
#include "netops.h"
#include "posix.h"
#include "buffer.h"
+#include "protocol.h"
#include <ctype.h>
@@ -130,6 +131,42 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int data_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_data *pkt;
+
+ line++;
+ len--;
+ pkt = git__malloc(sizeof(git_pkt_data) + len);
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_DATA;
+ pkt->len = (int) len;
+ memcpy(pkt->data, line, len);
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
+static int progress_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_progress *pkt;
+
+ line++;
+ len--;
+ pkt = git__malloc(sizeof(git_pkt_progress) + len);
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_PROGRESS;
+ pkt->len = (int) len;
+ memcpy(pkt->data, line, len);
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
/*
* Parse an other-ref line.
*/
@@ -263,8 +300,11 @@ int git_pkt_parse_line(
len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
- /* Assming the minimal size is actually 4 */
- if (!git__prefixcmp(line, "ACK"))
+ if (*line == GIT_SIDE_BAND_DATA)
+ ret = data_pkt(head, line, len);
+ else if (*line == GIT_SIDE_BAND_PROGRESS)
+ ret = progress_pkt(head, line, len);
+ else if (!git__prefixcmp(line, "ACK"))
ret = ack_pkt(head, line, len);
else if (!git__prefixcmp(line, "NAK"))
ret = nak_pkt(head);
@@ -301,6 +341,13 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
char oid[GIT_OID_HEXSZ +1] = {0};
unsigned int len;
+ /* Prefer side-band-64k if the server supports both */
+ if (caps->side_band) {
+ if (caps->side_band_64k)
+ git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
+ else
+ git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
+ }
if (caps->ofs_delta)
git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
diff --git a/src/pkt.h b/src/pkt.h
index 75442c83..0fdb5c7c 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -24,6 +24,8 @@ enum git_pkt_type {
GIT_PKT_PACK,
GIT_PKT_COMMENT,
GIT_PKT_ERR,
+ GIT_PKT_DATA,
+ GIT_PKT_PROGRESS,
};
/* Used for multi-ack */
@@ -67,6 +69,14 @@ typedef struct {
typedef struct {
enum git_pkt_type type;
+ int len;
+ char data[GIT_FLEX_ARRAY];
+} git_pkt_data;
+
+typedef git_pkt_data git_pkt_progress;
+
+typedef struct {
+ enum git_pkt_type type;
char error[GIT_FLEX_ARRAY];
} git_pkt_err;
diff --git a/src/posix.h b/src/posix.h
index d35fe08a..71bb8228 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -11,8 +11,15 @@
#include <fcntl.h>
#include <time.h>
+#ifndef S_IFGITLINK
#define S_IFGITLINK 0160000
#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+#endif
+
+/* if S_ISGID is not defined, then don't try to set it */
+#ifndef S_ISGID
+#define S_ISGID 0
+#endif
#if !defined(O_BINARY)
#define O_BINARY 0
diff --git a/src/protocol.c b/src/protocol.c
index 20d6e230..4526c857 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -80,6 +80,20 @@ int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps)
continue;
}
+ /* Keep side-band check after side-band-64k */
+ if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
+ caps->common = caps->side_band_64k = 1;
+ ptr += strlen(GIT_CAP_SIDE_BAND_64K);
+ continue;
+ }
+
+ if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
+ caps->common = caps->side_band = 1;
+ ptr += strlen(GIT_CAP_SIDE_BAND);
+ continue;
+ }
+
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
diff --git a/src/protocol.h b/src/protocol.h
index 615be8d6..a990938e 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -14,4 +14,8 @@
int git_protocol_store_refs(git_transport *t, int flushes);
int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps);
+#define GIT_SIDE_BAND_DATA 1
+#define GIT_SIDE_BAND_PROGRESS 2
+#define GIT_SIDE_BAND_ERROR 3
+
#endif
diff --git a/src/remote.c b/src/remote.c
index fe026b17..7bc631d4 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -386,6 +386,9 @@ int git_remote_connect(git_remote *remote, int direction)
if (git_transport_new(&t, url) < 0)
return -1;
+ t->progress_cb = remote->callbacks.progress;
+ t->cb_data = remote->callbacks.data;
+
t->check_cert = remote->check_cert;
if (t->connect(t, direction) < 0) {
goto on_error;
@@ -646,4 +649,9 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback
assert(remote && callbacks);
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
+
+ if (remote->transport) {
+ remote->transport->progress_cb = remote->callbacks.progress;
+ remote->transport->cb_data = remote->callbacks.data;
+ }
}
diff --git a/src/repo_template.h b/src/repo_template.h
new file mode 100644
index 00000000..ae5a9690
--- /dev/null
+++ b/src/repo_template.h
@@ -0,0 +1,58 @@
+/*
+ * 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_repo_template_h__
+#define INCLUDE_repo_template_h__
+
+#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
+#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
+
+#define GIT_HOOKS_DIR "hooks/"
+#define GIT_HOOKS_DIR_MODE 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"
+
+typedef struct {
+ const char *path;
+ mode_t mode;
+ const char *content;
+} repo_template_item;
+
+static repo_template_item repo_template[] = {
+ { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
+ { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
+ { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
+ { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
+ { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
+ { GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
+ { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
+ { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
+ { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
+ { NULL, 0, NULL }
+};
+
+#endif
diff --git a/src/repository.c b/src/repository.c
index 6f1f4349..c12df25c 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -18,15 +18,14 @@
#include "config.h"
#include "refs.h"
-#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_BRANCH_MASTER "master"
#define GIT_REPO_VERSION 0
+#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+
static void drop_odb(git_repository *repo)
{
if (repo->_odb != NULL) {
@@ -147,8 +146,13 @@ static int load_workdir(git_repository *repo, git_buf *parent_path)
return -1;
error = git_config_get_string(&worktree, config, "core.worktree");
- if (!error && worktree != NULL)
- repo->workdir = git__strdup(worktree);
+ if (!error && worktree != NULL) {
+ error = git_path_prettify_dir(
+ &worktree_buf, worktree, repo->path_repository);
+ if (error < 0)
+ return error;
+ repo->workdir = git_buf_detach(&worktree_buf);
+ }
else if (error != GIT_ENOTFOUND)
return error;
else {
@@ -238,16 +242,17 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
git_buf_rtrim(&file);
- if (file.size <= prefix_len ||
- memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
+ if (git_buf_len(&file) <= prefix_len ||
+ memcmp(git_buf_cstr(&file), 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;
+ const char *gitlink = git_buf_cstr(&file) + prefix_len;
while (*gitlink && git__isspace(*gitlink)) gitlink++;
- error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
+ error = git_path_prettify_dir(
+ path_out, gitlink, git_buf_cstr(path_out));
}
git_buf_free(&file);
@@ -359,9 +364,11 @@ int git_repository_open_ext(
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
git_repository *repo;
- *repo_ptr = NULL;
+ if (repo_ptr)
+ *repo_ptr = NULL;
- if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
+ error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
+ if (error < 0 || !repo_ptr)
return error;
repo = repository_alloc();
@@ -632,19 +639,35 @@ static int check_repositoryformatversion(git_config *config)
return 0;
}
-static int repo_init_createhead(const char *git_dir)
+static int repo_init_create_head(const char *git_dir, const char *ref_name)
{
git_buf ref_path = GIT_BUF_INIT;
git_filebuf ref = GIT_FILEBUF_INIT;
+ const char *fmt;
if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
- git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
- git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
+ git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
+ goto fail;
+
+ if (!ref_name)
+ ref_name = GIT_BRANCH_MASTER;
+
+ if (git__prefixcmp(ref_name, "refs/") == 0)
+ fmt = "ref: %s\n";
+ else
+ fmt = "ref: refs/heads/%s\n";
+
+ if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
- return -1;
+ goto fail;
git_buf_free(&ref_path);
return 0;
+
+fail:
+ git_buf_free(&ref_path);
+ git_filebuf_cleanup(&ref);
+ return -1;
}
static bool is_chmod_supported(const char *file_path)
@@ -665,6 +688,7 @@ static bool is_chmod_supported(const char *file_path)
return false;
_is_supported = (st1.st_mode != st2.st_mode);
+
return _is_supported;
}
@@ -686,19 +710,45 @@ cleanup:
return _is_insensitive;
}
-static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
+static bool are_symlinks_supported(const char *wd_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd;
+ struct stat st;
+ static int _symlinks_supported = -1;
+
+ if (_symlinks_supported > -1)
+ return _symlinks_supported;
+
+ if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
+ p_close(fd) < 0 ||
+ p_unlink(path.ptr) < 0 ||
+ p_symlink("testing", path.ptr) < 0 ||
+ p_lstat(path.ptr, &st) < 0)
+ _symlinks_supported = false;
+ else
+ _symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+
+ (void)p_unlink(path.ptr);
+ git_buf_free(&path);
+
+ return _symlinks_supported;
+}
+
+static int repo_init_config(
+ const char *repo_dir,
+ const char *work_dir,
+ git_repository_init_options *opts)
{
+ int error = 0;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
-#define SET_REPO_CONFIG(type, name, val) {\
- if (git_config_set_##type(config, name, val) < 0) { \
- git_buf_free(&cfg_path); \
- git_config_free(config); \
- return -1; } \
-}
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+ goto cleanup; } while (0)
- if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
return -1;
if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
@@ -706,58 +756,61 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
return -1;
}
- if (is_reinit && check_repositoryformatversion(config) < 0) {
- git_buf_free(&cfg_path);
- git_config_free(config);
- return -1;
- }
+ if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
+ (error = check_repositoryformatversion(config)) < 0)
+ goto cleanup;
+
+ SET_REPO_CONFIG(
+ bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
+ SET_REPO_CONFIG(
+ int32, "core.repositoryformatversion", GIT_REPO_VERSION);
+ SET_REPO_CONFIG(
+ bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
- SET_REPO_CONFIG(bool, "core.bare", is_bare);
- SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
- SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
-
- if (!is_bare)
+ if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!is_reinit && is_filesystem_case_insensitive(git_dir))
- SET_REPO_CONFIG(bool, "core.ignorecase", true);
- /* TODO: what other defaults? */
+ if (!are_symlinks_supported(work_dir))
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
- git_buf_free(&cfg_path);
- git_config_free(config);
- return 0;
-}
-
-#define GIT_HOOKS_DIR "hooks/"
-#define GIT_HOOKS_DIR_MODE 0755
+ if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ SET_REPO_CONFIG(string, "core.worktree", work_dir);
+ }
+ else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+ if ((error = git_config_delete(config, "core.worktree")) < 0)
+ goto cleanup;
+ }
+ } else {
+ if (!are_symlinks_supported(repo_dir))
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
+ }
-#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"
+ if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
+ is_filesystem_case_insensitive(repo_dir))
+ SET_REPO_CONFIG(bool, "core.ignorecase", true);
-#define GIT_INFO_DIR "info/"
-#define GIT_INFO_DIR_MODE 0755
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+ }
+ else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+ }
-#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"
+cleanup:
+ git_buf_free(&cfg_path);
+ git_config_free(config);
-#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"
+ return error;
+}
static int repo_write_template(
const char *git_dir,
bool allow_overwrite,
const char *file,
mode_t mode,
+ bool hidden,
const char *content)
{
git_buf path = GIT_BUF_INIT;
@@ -781,6 +834,15 @@ static int repo_write_template(
else if (errno != EEXIST)
error = fd;
+#ifdef GIT_WIN32
+ if (!error && hidden) {
+ if (p_hide_directory__w32(path.ptr) < 0)
+ error = -1;
+ }
+#else
+ GIT_UNUSED(hidden);
+#endif
+
git_buf_free(&path);
if (error)
@@ -790,86 +852,319 @@ static int repo_write_template(
return error;
}
-static int repo_init_structure(const char *git_dir, int is_bare)
+static int repo_write_gitlink(
+ const char *in_dir, const char *to_repo)
{
- 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 */
- if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ struct stat st;
+
+ git_path_dirname_r(&buf, to_repo);
+ git_path_to_dir(&buf);
+ if (git_buf_oom(&buf))
return -1;
- /* Hides the ".git" directory */
- if (!is_bare) {
+ /* don't write gitlink to natural workdir */
+ if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
+ strcmp(in_dir, buf.ptr) == 0)
+ {
+ error = GIT_PASSTHROUGH;
+ goto cleanup;
+ }
+
+ if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
+ goto cleanup;
+
+ if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
+ giterr_set(GITERR_REPOSITORY,
+ "Cannot overwrite gitlink file into path '%s'", in_dir);
+ error = GIT_EEXISTS;
+ goto cleanup;
+ }
+
+ git_buf_clear(&buf);
+
+ error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
+
+ if (!error)
+ error = repo_write_template(in_dir, true, DOT_GIT, 0644, true, buf.ptr);
+
+cleanup:
+ git_buf_free(&buf);
+ return error;
+}
+
+static mode_t pick_dir_mode(git_repository_init_options *opts)
+{
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
+ return 0755;
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
+ return (0775 | S_ISGID);
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
+ return (0777 | S_ISGID);
+ return opts->mode;
+}
+
+#include "repo_template.h"
+
+static int repo_init_structure(
+ const char *repo_dir,
+ const char *work_dir,
+ git_repository_init_options *opts)
+{
+ int error = 0;
+ repo_template_item *tpl;
+ bool external_tpl =
+ ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
+ mode_t dmode = pick_dir_mode(opts);
+
+ /* Hide the ".git" directory */
#ifdef GIT_WIN32
- if (p_hide_directory__w32(git_dir) < 0) {
+ if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
+ if (p_hide_directory__w32(repo_dir) < 0) {
giterr_set(GITERR_REPOSITORY,
"Failed to mark Git repository folder as hidden");
return -1;
}
-#endif
}
+#endif
- /* Make subdirectories as needed */
- for (i = 0; dirs[i].dir != NULL; ++i) {
- if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
+ /* Create the .git gitlink if appropriate */
+ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
+ (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
+ {
+ if (repo_write_gitlink(work_dir, repo_dir) < 0)
return -1;
}
- /* Make template files as needed */
- for (i = 0; tmpl[i].file != NULL; ++i) {
- if (repo_write_template(
- git_dir, false, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
+ /* Copy external template if requested */
+ if (external_tpl) {
+ git_config *cfg;
+ const char *tdir;
+
+ if (opts->template_path)
+ tdir = opts->template_path;
+ else if ((error = git_config_open_default(&cfg)) < 0)
+ return error;
+ else {
+ error = git_config_get_string(&tdir, cfg, "init.templatedir");
+
+ git_config_free(cfg);
+
+ if (error && error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
+ tdir = GIT_TEMPLATE_DIR;
+ }
+
+ error = git_futils_cp_r(tdir, repo_dir,
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD, dmode);
+
+ if (error < 0) {
+ if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+ return error;
+
+ /* if template was default, ignore error and use internal */
+ giterr_clear();
+ external_tpl = false;
+ }
+ }
+
+ /* Copy internal template
+ * - always ensure existence of dirs
+ * - only create files if no external template was specified
+ */
+ for (tpl = repo_template; !error && tpl->path; ++tpl) {
+ if (!tpl->content)
+ error = git_futils_mkdir(
+ tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
+ else if (!external_tpl) {
+ const char *content = tpl->content;
+
+ if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
+ content = opts->description;
+
+ error = repo_write_template(
+ repo_dir, false, tpl->path, tpl->mode, false, content);
+ }
+ }
+
+ return error;
+}
+
+static int repo_init_directories(
+ git_buf *repo_path,
+ git_buf *wd_path,
+ const char *given_repo,
+ git_repository_init_options *opts)
+{
+ int error = 0;
+ bool add_dotgit, has_dotgit, natural_wd;
+ mode_t dirmode;
+
+ /* set up repo path */
+
+ add_dotgit =
+ (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
+ (opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
+ git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
+ git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
+
+ if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
+ return -1;
+
+ has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
+ if (has_dotgit)
+ opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
+
+ /* set up workdir path */
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0) {
+ if (opts->workdir_path) {
+ if (git_path_join_unrooted(
+ wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
+ return -1;
+ } else if (has_dotgit) {
+ if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
+ return -1;
+ } else {
+ giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
+ " for non-bare repository that isn't a '.git' directory");
return -1;
+ }
+
+ if (git_path_to_dir(wd_path) < 0)
+ return -1;
+ } else {
+ git_buf_clear(wd_path);
}
- return 0;
+ natural_wd =
+ has_dotgit &&
+ wd_path->size > 0 &&
+ wd_path->size + strlen(GIT_DIR) == repo_path->size &&
+ memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
+ if (natural_wd)
+ opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
+
+ /* create directories as needed / requested */
+
+ dirmode = pick_dir_mode(opts);
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 && has_dotgit) {
+ git_buf p = GIT_BUF_INIT;
+ if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
+ error = git_futils_mkdir(p.ptr, NULL, dirmode, 0);
+ git_buf_free(&p);
+ }
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
+ has_dotgit)
+ {
+ uint32_t mkflag = GIT_MKDIR_CHMOD;
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
+ mkflag |= GIT_MKDIR_PATH;
+ error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, mkflag);
+ }
+
+ if (wd_path->size > 0 &&
+ !natural_wd &&
+ ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0))
+ error = git_futils_mkdir(wd_path->ptr, NULL, dirmode & ~S_ISGID,
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) ? GIT_MKDIR_PATH : 0);
+
+ /* prettify both directories now that they are created */
+
+ if (!error) {
+ error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);
+
+ if (!error && wd_path->size > 0)
+ error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
+ }
+
+ return error;
}
-int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
+static int repo_init_create_origin(git_repository *repo, const char *url)
{
- git_buf repository_path = GIT_BUF_INIT;
- bool is_reinit;
- int result = -1;
+ int error;
+ git_remote *remote;
- assert(repo_out && path);
+ if (!(error = git_remote_add(&remote, repo, "origin", url))) {
+ error = git_remote_save(remote);
+ git_remote_free(remote);
+ }
- if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
- goto cleanup;
+ return error;
+}
- is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path);
+int git_repository_init(
+ git_repository **repo_out, const char *path, unsigned is_bare)
+{
+ git_repository_init_options opts;
- if (is_reinit) {
- /* TODO: reinitialize the templates */
+ memset(&opts, 0, sizeof(opts));
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
+ if (is_bare)
+ opts.flags |= GIT_REPOSITORY_INIT_BARE;
- if (repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0)
- goto cleanup;
+ return git_repository_init_ext(repo_out, path, &opts);
+}
- } else if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
- repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 ||
- repo_init_createhead(repository_path.ptr) < 0) {
+int git_repository_init_ext(
+ git_repository **repo_out,
+ const char *given_repo,
+ git_repository_init_options *opts)
+{
+ int error;
+ git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
+
+ assert(repo_out && given_repo && opts);
+
+ error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
+ if (error < 0)
goto cleanup;
+
+ if (valid_repository_path(&repo_path)) {
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
+ giterr_set(GITERR_REPOSITORY,
+ "Attempt to reinitialize '%s'", given_repo);
+ error = GIT_EEXISTS;
+ goto cleanup;
+ }
+
+ opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
+
+ error = repo_init_config(
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
+
+ /* TODO: reinitialize the templates */
}
+ else {
+ if (!(error = repo_init_structure(
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
+ !(error = repo_init_config(
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
+ error = repo_init_create_head(
+ git_buf_cstr(&repo_path), opts->initial_head);
+ }
+ if (error < 0)
+ goto cleanup;
- result = git_repository_open(repo_out, repository_path.ptr);
+ error = git_repository_open(repo_out, git_buf_cstr(&repo_path));
+
+ if (!error && opts->origin_url)
+ error = repo_init_create_origin(*repo_out, opts->origin_url);
cleanup:
- git_buf_free(&repository_path);
- return result;
+ git_buf_free(&repo_path);
+ git_buf_free(&wd_path);
+
+ return error;
}
int git_repository_head_detached(git_repository *repo)
@@ -965,43 +1260,6 @@ const char *git_repository_workdir(git_repository *repo)
return repo->workdir;
}
-static int write_gitlink(
- const char *in_dir, const char *to_repo)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- struct stat st;
-
- if (git_path_dirname_r(&buf, to_repo) < 0 ||
- git_path_to_dir(&buf) < 0)
- return -1;
-
- /* don't write gitlink to natural workdir */
- if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
- strcmp(in_dir, buf.ptr) == 0)
- return GIT_PASSTHROUGH;
-
- if (git_buf_joinpath(&buf, in_dir, DOT_GIT) < 0)
- return -1;
-
- if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
- giterr_set(GITERR_REPOSITORY,
- "Cannot overwrite gitlink file into path '%s'", in_dir);
- return GIT_EEXISTS;
- }
-
- git_buf_clear(&buf);
-
- if (git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo) < 0)
- return -1;
-
- error = repo_write_template(in_dir, true, DOT_GIT, 0644, buf.ptr);
-
- git_buf_free(&buf);
-
- return error;
-}
-
int git_repository_set_workdir(
git_repository *repo, const char *workdir, int update_gitlink)
{
@@ -1022,7 +1280,7 @@ int git_repository_set_workdir(
if (git_repository_config__weakptr(&config, repo) < 0)
return -1;
- error = write_gitlink(path.ptr, git_repository_path(repo));
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo));
/* passthrough error means gitlink is unnecessary */
if (error == GIT_PASSTHROUGH)
@@ -1078,39 +1336,27 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo)
{
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
struct stat st;
- ssize_t size;
int error;
if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0)
return -1;
- error = p_stat(git_buf_cstr(&path), &st);
- if (error < 0) {
+ if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
if (errno == ENOENT)
error = GIT_ENOTFOUND;
-
- git_buf_free(&path);
- return error;
}
-
- if (buffer == NULL) {
- git_buf_free(&path);
- return (int)st.st_size;
+ else if (buffer != NULL) {
+ error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
+ git_buf_copy_cstr(buffer, len, &buf);
}
- if (git_futils_readbuffer(&buf, git_buf_cstr(&path)) < 0)
- goto on_error;
-
- memcpy(buffer, git_buf_cstr(&buf), len);
- size = git_buf_len(&buf);
-
git_buf_free(&path);
git_buf_free(&buf);
- return size;
-on_error:
- git_buf_free(&path);
- return -1;
+ if (!error)
+ error = (int)st.st_size + 1; /* add 1 for NUL byte */
+
+ return error;
}
int git_repository_message_remove(git_repository *repo)
diff --git a/src/repository.h b/src/repository.h
index 4e03e632..4695edf3 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -68,6 +68,13 @@ typedef enum {
GIT_EOL_DEFAULT = GIT_EOL_NATIVE
} git_cvar_value;
+/* internal repository init flags */
+enum {
+ GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16),
+ GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17),
+ GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18),
+};
+
/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
@@ -75,6 +82,7 @@ struct git_object {
git_otype type;
};
+/** Internal structure for repository object */
struct git_repository {
git_odb *_odb;
git_config *_config;
@@ -94,8 +102,7 @@ struct git_repository {
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
-/* fully free the object; internal method, do not
- * export */
+/* fully free the object; internal method, DO NOT EXPORT */
void git_object__free(void *object);
GIT_INLINE(int) git_object__dup(git_object **dest, git_object *source)
diff --git a/src/submodule.c b/src/submodule.c
index b8537cb8..a9de9ee6 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -17,18 +17,24 @@
#include "config_file.h"
#include "config.h"
#include "repository.h"
+#include "submodule.h"
+#include "tree.h"
+#include "iterator.h"
+
+#define GIT_MODULES_FILE ".gitmodules"
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}
+ {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
};
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, "none", GIT_SUBMODULE_IGNORE_NONE},
{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
- {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
+ {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+ {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
};
static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
@@ -36,7 +42,7 @@ static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
khint_t h;
for (h = 0; *s; ++s)
- if (s[1] || *s != '/')
+ if (s[1] != '\0' || *s != '/')
h = (h << 5) - h + *s;
return h;
@@ -47,29 +53,790 @@ static kh_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] == '/')
+ if (alen > 0 && a[alen - 1] == '/')
alen--;
- if (blen && b[blen] == '/')
+ if (blen > 0 && b[blen - 1] == '/')
blen--;
return (alen == blen && strncmp(a, b, alen) == 0);
}
-__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+__KHASH_IMPL(
+ str, static kh_inline, const char *, void *, 1,
+ str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+
+static int load_submodule_config(git_repository *repo, bool force);
+static git_config_file *open_gitmodules(git_repository *, bool, const git_oid *);
+static int lookup_head_remote(git_buf *url, git_repository *repo);
+static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
+static void submodule_release(git_submodule *sm, int decr);
+static int submodule_load_from_index(git_repository *, const git_index_entry *);
+static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
+static int submodule_load_from_config(const char *, const char *, void *);
+static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
+static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
+static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
+static int submodule_index_status(unsigned int *status, git_submodule *sm);
+static int submodule_wd_status(unsigned int *status, git_submodule *sm);
-static git_submodule *submodule_alloc(const char *name)
+static int submodule_cmp(const void *a, const void *b)
{
- git_submodule *sm = git__calloc(1, sizeof(git_submodule));
- if (sm == NULL)
- return sm;
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
- sm->path = sm->name = git__strdup(name);
- if (!sm->name) {
- git__free(sm);
+static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
+{
+ ssize_t idx = git_buf_rfind(key, '.');
+ git_buf_truncate(key, (size_t)(idx + 1));
+ return git_buf_puts(key, suffix);
+}
+
+/*
+ * PUBLIC APIS
+ */
+
+int git_submodule_lookup(
+ git_submodule **sm_ptr, /* NULL if user only wants to test existence */
+ git_repository *repo,
+ const char *name) /* trailing slash is allowed */
+{
+ int error;
+ khiter_t pos;
+
+ assert(repo && name);
+
+ if ((error = load_submodule_config(repo, false)) < 0)
+ return error;
+
+ pos = git_strmap_lookup_index(repo->submodules, name);
+
+ if (!git_strmap_valid_index(repo->submodules, pos)) {
+ error = GIT_ENOTFOUND;
+
+ /* check if a plausible submodule exists at path */
+ if (git_repository_workdir(repo)) {
+ git_buf path = GIT_BUF_INIT;
+
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0)
+ return -1;
+
+ if (git_path_contains_dir(&path, DOT_GIT))
+ error = GIT_EEXISTS;
+
+ git_buf_free(&path);
+ }
+
+ return error;
+ }
+
+ if (sm_ptr)
+ *sm_ptr = git_strmap_value_at(repo->submodules, pos);
+
+ return 0;
+}
+
+int git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(git_submodule *sm, const char *name, void *payload),
+ void *payload)
+{
+ int error;
+ git_submodule *sm;
+ git_vector seen = GIT_VECTOR_INIT;
+ seen._cmp = submodule_cmp;
+
+ assert(repo && callback);
+
+ if ((error = load_submodule_config(repo, false)) < 0)
+ return error;
+
+ git_strmap_foreach_value(repo->submodules, sm, {
+ /* Usually the following will not come into play - it just prevents
+ * us from issuing a callback twice for a submodule where the name
+ * and path are not the same.
+ */
+ if (sm->refcount > 1) {
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
+ continue;
+ if ((error = git_vector_insert(&seen, sm)) < 0)
+ break;
+ }
+
+ if (callback(sm, sm->name, payload)) {
+ error = GIT_EUSER;
+ break;
+ }
+ });
+
+ git_vector_free(&seen);
+
+ return error;
+}
+
+void git_submodule_config_free(git_repository *repo)
+{
+ git_strmap *smcfg;
+ git_submodule *sm;
+
+ assert(repo);
+
+ smcfg = repo->submodules;
+ repo->submodules = NULL;
+
+ if (smcfg == NULL)
+ return;
+
+ git_strmap_foreach_value(smcfg, sm, {
+ submodule_release(sm,1);
+ });
+ git_strmap_free(smcfg);
+}
+
+int git_submodule_add_setup(
+ git_submodule **submodule,
+ git_repository *repo,
+ const char *url,
+ const char *path,
+ int use_gitlink)
+{
+ int error = 0;
+ git_config_file *mods = NULL;
+ git_submodule *sm;
+ git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
+ git_repository_init_options initopt;
+ git_repository *subrepo = NULL;
+
+ assert(repo && url && path);
+
+ /* see if there is already an entry for this submodule */
+
+ if (git_submodule_lookup(&sm, repo, path) < 0)
+ giterr_clear();
+ else {
+ giterr_set(GITERR_SUBMODULE,
+ "Attempt to add a submodule that already exists");
+ return GIT_EEXISTS;
+ }
+
+ /* resolve parameters */
+
+ if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) {
+ if (!(error = lookup_head_remote(&real_url, repo)))
+ error = git_path_apply_relative(&real_url, url);
+ } else if (strchr(url, ':') != NULL || url[0] == '/') {
+ error = git_buf_sets(&real_url, url);
+ } else {
+ giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
+ error = -1;
+ }
+ if (error)
+ goto cleanup;
+
+ /* validate and normalize path */
+
+ if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
+ path += strlen(git_repository_workdir(repo));
+
+ if (git_path_root(path) >= 0) {
+ giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* update .gitmodules */
+
+ if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
+ giterr_set(GITERR_SUBMODULE,
+ "Adding submodules to a bare repository is not supported (for now)");
+ return -1;
+ }
+
+ if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
+ (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
+ goto cleanup;
+
+ if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
+ (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
+ goto cleanup;
+
+ git_buf_clear(&name);
+
+ /* init submodule repository and add origin remote as needed */
+
+ error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
+ if (error < 0)
+ goto cleanup;
+
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+ * gitlink in the sub-repo workdir directory to that repository
+ *
+ * Old style: sub-repo goes directly into repo/<name>/.git/
+ */
+
+ memset(&initopt, 0, sizeof(initopt));
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_REINIT;
+ initopt.origin_url = real_url.ptr;
+
+ if (git_path_exists(name.ptr) &&
+ git_path_contains(&name, DOT_GIT))
+ {
+ /* repo appears to already exist - reinit? */
+ }
+ else if (use_gitlink) {
+ git_buf repodir = GIT_BUF_INIT;
+
+ error = git_buf_join_n(
+ &repodir, '/', 3, git_repository_path(repo), "modules", path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = name.ptr;
+ initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+
+ git_buf_free(&repodir);
+ }
+ else {
+ error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
+ }
+ if (error < 0)
+ goto cleanup;
+
+ /* add submodule to hash and "reload" it */
+
+ if (!(error = submodule_get(&sm, repo, path, NULL)) &&
+ !(error = git_submodule_reload(sm)))
+ error = git_submodule_init(sm, false);
+
+cleanup:
+ if (submodule != NULL)
+ *submodule = !error ? sm : NULL;
+
+ if (mods != NULL)
+ git_config_file_free(mods);
+ git_repository_free(subrepo);
+ git_buf_free(&real_url);
+ git_buf_free(&name);
+
+ return error;
+}
+
+int git_submodule_add_finalize(git_submodule *sm)
+{
+ int error;
+ git_index *index;
+
+ assert(sm);
+
+ if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
+ (error = git_index_add(index, GIT_MODULES_FILE, 0)) < 0)
+ return error;
+
+ return git_submodule_add_to_index(sm, true);
+}
+
+int git_submodule_add_to_index(git_submodule *sm, int write_index)
+{
+ int error;
+ git_repository *repo, *sm_repo;
+ git_index *index;
+ git_buf path = GIT_BUF_INIT;
+ git_commit *head;
+ git_index_entry entry;
+ struct stat st;
+
+ assert(sm);
+
+ repo = sm->owner;
+
+ /* force reload of wd OID by git_submodule_open */
+ sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_buf_joinpath(
+ &path, git_repository_workdir(repo), sm->path)) < 0 ||
+ (error = git_submodule_open(&sm_repo, sm)) < 0)
+ goto cleanup;
+
+ /* read stat information for submodule working directory */
+ if (p_stat(path.ptr, &st) < 0) {
+ giterr_set(GITERR_SUBMODULE,
+ "Cannot add submodule without working directory");
+ error = -1;
+ goto cleanup;
+ }
+
+ memset(&entry, 0, sizeof(entry));
+ entry.path = sm->path;
+ git_index__init_entry_from_stat(&st, &entry);
+
+ /* calling git_submodule_open will have set sm->wd_oid if possible */
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
+ giterr_set(GITERR_SUBMODULE,
+ "Cannot add submodule without HEAD to index");
+ error = -1;
+ goto cleanup;
+ }
+ git_oid_cpy(&entry.oid, &sm->wd_oid);
+
+ if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
+ goto cleanup;
+
+ entry.ctime.seconds = git_commit_time(head);
+ entry.ctime.nanoseconds = 0;
+ entry.mtime.seconds = git_commit_time(head);
+ entry.mtime.nanoseconds = 0;
+
+ git_commit_free(head);
+
+ /* add it */
+ error = git_index_add2(index, &entry);
+
+ /* write it, if requested */
+ if (!error && write_index) {
+ error = git_index_write(index);
+
+ if (!error)
+ git_oid_cpy(&sm->index_oid, &sm->wd_oid);
+ }
+
+cleanup:
+ git_repository_free(sm_repo);
+ git_buf_free(&path);
+ return error;
+}
+
+int git_submodule_save(git_submodule *submodule)
+{
+ int error = 0;
+ git_config_file *mods;
+ git_buf key = GIT_BUF_INIT;
+
+ assert(submodule);
+
+ mods = open_gitmodules(submodule->owner, true, NULL);
+ if (!mods) {
+ giterr_set(GITERR_SUBMODULE,
+ "Adding submodules to a bare repository is not supported (for now)");
+ return -1;
+ }
+
+ if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0)
+ goto cleanup;
+
+ /* save values for path, url, update, ignore, fetchRecurseSubmodules */
+
+ if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 ||
+ (error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0)
+ goto cleanup;
+
+ if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 ||
+ (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0)
+ goto cleanup;
+
+ if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
+ submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
+ {
+ const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+ NULL : _sm_update_map[submodule->update].str_match;
+ error = git_config_file_set_string(mods, key.ptr, val);
+ }
+ if (error < 0)
+ goto cleanup;
+
+ if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
+ submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
+ {
+ const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
+ NULL : _sm_ignore_map[submodule->ignore].str_match;
+ error = git_config_file_set_string(mods, key.ptr, val);
+ }
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = submodule_config_key_trunc_puts(
+ &key, "fetchRecurseSubmodules")) < 0 ||
+ (error = git_config_file_set_string(
+ mods, key.ptr, submodule->fetch_recurse ? "true" : "false")) < 0)
+ goto cleanup;
+
+ /* update internal defaults */
+
+ submodule->ignore_default = submodule->ignore;
+ submodule->update_default = submodule->update;
+ submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+
+cleanup:
+ if (mods != NULL)
+ git_config_file_free(mods);
+ git_buf_free(&key);
+
+ return error;
+}
+
+git_repository *git_submodule_owner(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->owner;
+}
+
+const char *git_submodule_name(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->name;
+}
+
+const char *git_submodule_path(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->path;
+}
+
+const char *git_submodule_url(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->url;
+}
+
+int git_submodule_set_url(git_submodule *submodule, const char *url)
+{
+ assert(submodule && url);
+
+ git__free(submodule->url);
+
+ submodule->url = git__strdup(url);
+ GITERR_CHECK_ALLOC(submodule->url);
+
+ return 0;
+}
+
+const git_oid *git_submodule_index_oid(git_submodule *submodule)
+{
+ assert(submodule);
+
+ if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
+ return &submodule->index_oid;
+ else
+ return NULL;
+}
+
+const git_oid *git_submodule_head_oid(git_submodule *submodule)
+{
+ assert(submodule);
+
+ if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
+ return &submodule->head_oid;
+ else
return NULL;
+}
+
+const git_oid *git_submodule_wd_oid(git_submodule *submodule)
+{
+ assert(submodule);
+
+ if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
+ git_repository *subrepo;
+
+ /* calling submodule open grabs the HEAD OID if possible */
+ if (!git_submodule_open(&subrepo, submodule))
+ git_repository_free(subrepo);
+ else
+ giterr_clear();
}
+ if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
+ return &submodule->wd_oid;
+ else
+ return NULL;
+}
+
+git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->ignore;
+}
+
+git_submodule_ignore_t git_submodule_set_ignore(
+ git_submodule *submodule, git_submodule_ignore_t ignore)
+{
+ git_submodule_ignore_t old;
+
+ assert(submodule);
+
+ if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
+ ignore = submodule->ignore_default;
+
+ old = submodule->ignore;
+ submodule->ignore = ignore;
+ return old;
+}
+
+git_submodule_update_t git_submodule_update(git_submodule *submodule)
+{
+ assert(submodule);
+ return submodule->update;
+}
+
+git_submodule_update_t git_submodule_set_update(
+ git_submodule *submodule, git_submodule_update_t update)
+{
+ git_submodule_update_t old;
+
+ assert(submodule);
+
+ if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
+ update = submodule->update_default;
+
+ old = submodule->update;
+ submodule->update = update;
+ return old;
+}
+
+int git_submodule_init(git_submodule *submodule, int overwrite)
+{
+ int error;
+
+ /* write "submodule.NAME.url" */
+
+ if (!submodule->url) {
+ giterr_set(GITERR_SUBMODULE,
+ "No URL configured for submodule '%s'", submodule->name);
+ return -1;
+ }
+
+ error = submodule_update_config(
+ submodule, "url", submodule->url, overwrite != 0, false);
+ if (error < 0)
+ return error;
+
+ /* write "submodule.NAME.update" if not default */
+
+ if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
+ error = submodule_update_config(
+ submodule, "update", NULL, (overwrite != 0), false);
+ else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
+ error = submodule_update_config(
+ submodule, "update",
+ _sm_update_map[submodule->update].str_match,
+ (overwrite != 0), false);
+
+ return error;
+}
+
+int git_submodule_sync(git_submodule *submodule)
+{
+ if (!submodule->url) {
+ giterr_set(GITERR_SUBMODULE,
+ "No URL configured for submodule '%s'", submodule->name);
+ return -1;
+ }
+
+ /* copy URL over to config only if it already exists */
+
+ return submodule_update_config(
+ submodule, "url", submodule->url, true, true);
+}
+
+int git_submodule_open(
+ git_repository **subrepo,
+ git_submodule *submodule)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+ const char *workdir;
+
+ assert(submodule && subrepo);
+
+ repo = submodule->owner;
+ workdir = git_repository_workdir(repo);
+
+ if (!workdir) {
+ giterr_set(GITERR_REPOSITORY,
+ "Cannot open submodule repository in a bare repo");
+ return GIT_ENOTFOUND;
+ }
+
+ if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
+ giterr_set(GITERR_REPOSITORY,
+ "Cannot open submodule repository that is not checked out");
+ return GIT_ENOTFOUND;
+ }
+
+ if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
+ return -1;
+
+ error = git_repository_open(subrepo, path.ptr);
+
+ git_buf_free(&path);
+
+ /* if we have opened the submodule successfully, let's grab the HEAD OID */
+ if (!error && !(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
+ if (!git_reference_name_to_oid(
+ &submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
+ submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
+ else
+ giterr_clear();
+ }
+
+ return error;
+}
+
+int git_submodule_reload_all(git_repository *repo)
+{
+ assert(repo);
+ return load_submodule_config(repo, true);
+}
+
+int git_submodule_reload(git_submodule *submodule)
+{
+ git_repository *repo;
+ git_index *index;
+ int pos, error;
+ git_tree *head;
+ git_config_file *mods;
+
+ assert(submodule);
+
+ /* refresh index data */
+
+ repo = submodule->owner;
+ if (git_repository_index__weakptr(&index, repo) < 0)
+ return -1;
+
+ submodule->flags = submodule->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
+
+ pos = git_index_find(index, submodule->path);
+ if (pos >= 0) {
+ git_index_entry *entry = git_index_get(index, pos);
+
+ if (S_ISGITLINK(entry->mode)) {
+ if ((error = submodule_load_from_index(repo, entry)) < 0)
+ return error;
+ } else {
+ submodule_mode_mismatch(
+ repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
+ }
+ }
+
+ /* refresh HEAD tree data */
+
+ if (!(error = git_repository_head_tree(&head, repo))) {
+ git_tree_entry *te;
+
+ submodule->flags = submodule->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
+
+ if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
+
+ if (S_ISGITLINK(te->attr)) {
+ error = submodule_load_from_head(repo, submodule->path, &te->oid);
+ } else {
+ submodule_mode_mismatch(
+ repo, submodule->path,
+ GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
+ }
+
+ git_tree_entry_free(te);
+ }
+ else if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ }
+
+ git_tree_free(head);
+ }
+
+ if (error < 0)
+ return error;
+
+ /* refresh config data */
+
+ if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
+ git_buf path = GIT_BUF_INIT;
+
+ git_buf_sets(&path, "submodule\\.");
+ git_buf_puts_escape_regex(&path, submodule->name);
+ git_buf_puts(&path, ".*");
+
+ if (git_buf_oom(&path))
+ error = -1;
+ else
+ error = git_config_file_foreach_match(
+ mods, path.ptr, submodule_load_from_config, repo);
+
+ git_buf_free(&path);
+ git_config_file_free(mods);
+ }
+
+ if (error < 0)
+ return error;
+
+ /* refresh wd data */
+
+ submodule->flags = submodule->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
+
+ error = submodule_load_from_wd_lite(submodule, submodule->path, NULL);
+
+ return error;
+}
+
+int git_submodule_status(
+ unsigned int *status,
+ git_submodule *submodule)
+{
+ int error = 0;
+ unsigned int status_val;
+
+ assert(status && submodule);
+
+ status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
+
+ if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
+ if (!(error = submodule_index_status(&status_val, submodule)))
+ error = submodule_wd_status(&status_val, submodule);
+ }
+
+ *status = status_val;
+
+ return error;
+}
+
+/*
+ * INTERNAL FUNCTIONS
+ */
+
+static git_submodule *submodule_alloc(git_repository *repo, const char *name)
+{
+ git_submodule *sm;
+
+ if (!name || !strlen(name)) {
+ giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
+ return NULL;
+ }
+
+ sm = git__calloc(1, sizeof(git_submodule));
+ if (sm == NULL)
+ goto fail;
+
+ sm->path = sm->name = git__strdup(name);
+ if (!sm->name)
+ goto fail;
+
+ sm->owner = repo;
+ sm->refcount = 1;
+
return sm;
+
+fail:
+ submodule_release(sm, 0);
+ return NULL;
}
static void submodule_release(git_submodule *sm, int decr)
@@ -80,70 +847,121 @@ static void submodule_release(git_submodule *sm, int decr)
sm->refcount -= decr;
if (sm->refcount == 0) {
- if (sm->name != sm->path)
+ if (sm->name != sm->path) {
git__free(sm->path);
+ sm->path = NULL;
+ }
+
git__free(sm->name);
+ sm->name = NULL;
+
git__free(sm->url);
+ sm->url = NULL;
+
+ sm->owner = NULL;
+
git__free(sm);
}
}
-static int submodule_from_entry(
- git_strmap *smcfg, git_index_entry *entry)
+static int submodule_get(
+ git_submodule **sm_ptr,
+ git_repository *repo,
+ const char *name,
+ const char *alternate)
{
- git_submodule *sm;
- void *old_sm;
+ git_strmap *smcfg = repo->submodules;
khiter_t pos;
+ git_submodule *sm;
int error;
- pos = git_strmap_lookup_index(smcfg, entry->path);
+ assert(repo && name);
- if (git_strmap_valid_index(smcfg, pos))
- sm = git_strmap_value_at(smcfg, pos);
- else
- sm = submodule_alloc(entry->path);
+ pos = git_strmap_lookup_index(smcfg, name);
- git_oid_cpy(&sm->oid, &entry->oid);
+ if (!git_strmap_valid_index(smcfg, pos) && alternate)
+ pos = git_strmap_lookup_index(smcfg, alternate);
- if (strcmp(sm->path, entry->path) != 0) {
- if (sm->path != sm->name) {
- git__free(sm->path);
- sm->path = sm->name;
+ if (!git_strmap_valid_index(smcfg, pos)) {
+ sm = submodule_alloc(repo, name);
+
+ /* insert value at name - if another thread beats us to it, then use
+ * their record and release our own.
+ */
+ pos = kh_put(str, smcfg, sm->name, &error);
+
+ if (error < 0) {
+ submodule_release(sm, 1);
+ sm = NULL;
+ } else if (error == 0) {
+ submodule_release(sm, 1);
+ sm = git_strmap_value_at(smcfg, pos);
+ } else {
+ git_strmap_set_value_at(smcfg, pos, sm);
}
- sm->path = git__strdup(entry->path);
- if (!sm->path)
- goto fail;
+ } else {
+ sm = git_strmap_value_at(smcfg, pos);
}
- git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
- if (error < 0)
- goto fail;
- sm->refcount++;
+ *sm_ptr = sm;
- if (old_sm && ((git_submodule *)old_sm) != sm) {
- /* TODO: log warning about multiple entrys for same submodule path */
- submodule_release(old_sm, 1);
+ return (sm != NULL) ? 0 : -1;
+}
+
+static int submodule_load_from_index(
+ git_repository *repo, const git_index_entry *entry)
+{
+ git_submodule *sm;
+
+ if (submodule_get(&sm, repo, entry->path, NULL) < 0)
+ return -1;
+
+ if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
+ return 0;
}
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
+
+ git_oid_cpy(&sm->index_oid, &entry->oid);
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
+
return 0;
+}
-fail:
- submodule_release(sm, 0);
+static int submodule_load_from_head(
+ git_repository *repo, const char *path, const git_oid *oid)
+{
+ git_submodule *sm;
+
+ if (submodule_get(&sm, repo, path, NULL) < 0)
+ return -1;
+
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
+
+ git_oid_cpy(&sm->head_oid, oid);
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
+
+ return 0;
+}
+
+static int submodule_config_error(const char *property, const char *value)
+{
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule '%s' property: '%s'", property, value);
return -1;
}
-static int submodule_from_config(
+static int submodule_load_from_config(
const char *key, const char *value, void *data)
{
- git_strmap *smcfg = data;
- const char *namestart;
- const char *property;
+ git_repository *repo = data;
+ git_strmap *smcfg = repo->submodules;
+ const char *namestart, *property, *alternate = NULL;
git_buf name = GIT_BUF_INIT;
git_submodule *sm;
- void *old_sm = NULL;
bool is_path;
- khiter_t pos;
- int error;
+ int error = 0;
if (git__prefixcmp(key, "submodule.") != 0)
return 0;
@@ -153,235 +971,502 @@ static int submodule_from_config(
if (property == NULL)
return 0;
property++;
- is_path = (strcmp(property, "path") == 0);
+ is_path = (strcasecmp(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 (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
+ git_buf_free(&name);
+ return -1;
+ }
- if (strcmp(sm->name, name.ptr) != 0) {
- assert(sm->path == sm->name);
- sm->name = git_buf_detach(&name);
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
- git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
- if (error < 0)
- goto fail;
- sm->refcount++;
+ /* Only from config might we get differing names & paths. If so, then
+ * update the submodule and insert under the alternative key.
+ */
+
+ /* TODO: if case insensitive filesystem, then the following strcmps
+ * should be strcasecmp
+ */
+
+ if (strcmp(sm->name, name.ptr) != 0) {
+ alternate = sm->name = git_buf_detach(&name);
+ } else if (is_path && value && strcmp(sm->path, value) != 0) {
+ alternate = sm->path = git__strdup(value);
+ if (!sm->path)
+ error = -1;
}
- 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;
+ if (alternate) {
+ void *old_sm = NULL;
+ git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
- git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
- if (error < 0)
- goto fail;
- sm->refcount++;
+ if (error >= 0)
+ sm->refcount++; /* inserted under a new key */
+
+ /* if we replaced an old module under this key, release the old one */
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ submodule_release(old_sm, 1);
+ /* TODO: log warning about multiple submodules with same path */
+ }
}
+
git_buf_free(&name);
+ if (error < 0)
+ return error;
- if (old_sm && ((git_submodule *)old_sm) != sm) {
- /* TODO: log warning about multiple submodules with same path */
- submodule_release(old_sm, 1);
- }
+ /* TODO: Look up path in index and if it is present but not a GITLINK
+ * then this should be deleted (at least to match git's behavior)
+ */
if (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;
+ if (strcasecmp(property, "url") == 0) {
+ git__free(sm->url);
+ sm->url = NULL;
+
+ if (value != NULL && (sm->url = git__strdup(value)) == NULL)
+ return -1;
}
- else if (strcmp(property, "update") == 0) {
+ else if (strcasecmp(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;
+ _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0)
+ return submodule_config_error("update", value);
+ sm->update_default = 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 (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
+ if (git__parse_bool(&sm->fetch_recurse, value) < 0)
+ return submodule_config_error("fetchRecurseSubmodules", value);
}
- else if (strcmp(property, "ignore") == 0) {
+ else if (strcasecmp(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;
+ _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0)
+ return submodule_config_error("ignore", value);
+ sm->ignore_default = 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 submodule_load_from_wd_lite(
+ git_submodule *sm, const char *name, void *payload)
+{
+ git_repository *repo = git_submodule_owner(sm);
+ git_buf path = GIT_BUF_INIT;
+
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
+ return -1;
+
+ if (git_path_isdir(path.ptr))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+ if (git_path_contains(&path, DOT_GIT))
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
+
+ git_buf_free(&path);
+
+ return 0;
+}
+
+static void submodule_mode_mismatch(
+ git_repository *repo, const char *path, unsigned int flag)
+{
+ khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
+
+ if (git_strmap_valid_index(repo->submodules, pos)) {
+ git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
+
+ sm->flags |= flag;
+ }
}
-static int load_submodule_config(git_repository *repo)
+static int load_submodule_config_from_index(
+ git_repository *repo, git_oid *gitmodules_oid)
{
int error;
- git_index *index;
- unsigned int i, max_i;
- git_oid gitmodules_oid;
- git_strmap *smcfg;
- struct git_config_file *mods = NULL;
+ git_iterator *i;
+ const git_index_entry *entry;
- if (repo->submodules)
- return 0;
+ if ((error = git_iterator_for_index(&i, repo)) < 0)
+ return error;
- /* 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);
+ error = git_iterator_current(i, &entry);
- /* 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);
+ while (!error && entry != NULL) {
+
+ if (S_ISGITLINK(entry->mode)) {
+ error = submodule_load_from_index(repo, entry);
+ if (error < 0)
+ break;
+ } else {
+ submodule_mode_mismatch(
+ repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
+
+ if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
+ git_oid_cpy(gitmodules_oid, &entry->oid);
+ }
+
+ error = git_iterator_advance(i, &entry);
+ }
+
+ git_iterator_free(i);
+
+ return error;
+}
+
+static int load_submodule_config_from_head(
+ git_repository *repo, git_oid *gitmodules_oid)
+{
+ int error;
+ git_tree *head;
+ git_iterator *i;
+ const git_index_entry *entry;
+
+ if ((error = git_repository_head_tree(&head, repo)) < 0)
+ return error;
+
+ if ((error = git_iterator_for_tree(&i, repo, head)) < 0) {
+ git_tree_free(head);
+ return error;
+ }
+
+ error = git_iterator_current(i, &entry);
+
+ while (!error && entry != NULL) {
- 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;
+ error = submodule_load_from_head(repo, entry->path, &entry->oid);
+ if (error < 0)
+ break;
+ } else {
+ submodule_mode_mismatch(
+ repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
+
+ if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
+ git_oid_iszero(gitmodules_oid))
+ git_oid_cpy(gitmodules_oid, &entry->oid);
}
- else if (strcmp(entry->path, ".gitmodules") == 0)
- git_oid_cpy(&gitmodules_oid, &entry->oid);
+
+ error = git_iterator_advance(i, &entry);
}
- /* 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_iterator_free(i);
+ git_tree_free(head);
+
+ return error;
+}
+
+static git_config_file *open_gitmodules(
+ git_repository *repo,
+ bool okay_to_create,
+ const git_oid *gitmodules_oid)
+{
+ const char *workdir = git_repository_workdir(repo);
+ git_buf path = GIT_BUF_INIT;
+ git_config_file *mods = NULL;
+
+ if (workdir != NULL) {
+ if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
+ return NULL;
+
+ if (okay_to_create || git_path_isfile(path.ptr)) {
+ /* git_config_file__ondisk should only fail if OOM */
+ if (git_config_file__ondisk(&mods, path.ptr) < 0)
+ mods = NULL;
+ /* open should only fail here if the file is malformed */
+ else if (git_config_file_open(mods) < 0) {
+ git_config_file_free(mods);
+ mods = NULL;
+ }
}
- 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? */
+ if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
+ /* TODO: Retrieve .gitmodules content from ODB */
+
+ /* Should we actually do this? Core git does not, but it means you
+ * can't really get much information about submodules on bare repos.
+ */
+ }
+
+ git_buf_free(&path);
+
+ return mods;
+}
+
+static int load_submodule_config(git_repository *repo, bool force)
+{
+ int error;
+ git_oid gitmodules_oid;
+ git_buf path = GIT_BUF_INIT;
+ git_config_file *mods = NULL;
+
+ if (repo->submodules && !force)
+ return 0;
+
+ memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
+
+ /* Submodule data is kept in a hashtable keyed by both name and path.
+ * These are usually the same, but that is not guaranteed.
+ */
+ if (!repo->submodules) {
+ repo->submodules = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(repo->submodules);
}
- /* process .gitmodules info */
- if (!error && mods != NULL)
- error = git_config_file_foreach(mods, submodule_from_config, smcfg);
+ /* add submodule information from index */
+
+ if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
+ goto cleanup;
+
+ /* add submodule information from HEAD */
+
+ if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
+ goto cleanup;
+
+ /* add submodule information from .gitmodules */
- /* store submodule config in repo */
- if (!error)
- repo->submodules = smcfg;
+ if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
+ error = git_config_file_foreach(mods, submodule_load_from_config, repo);
+
+ if (error != 0)
+ goto cleanup;
+
+ /* shallow scan submodules in work tree */
+
+ if (!git_repository_is_bare(repo))
+ error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
cleanup:
+ git_buf_free(&path);
+
if (mods != NULL)
git_config_file_free(mods);
+
if (error)
- git_strmap_free(smcfg);
+ git_submodule_config_free(repo);
+
return error;
}
-void git_submodule_config_free(git_repository *repo)
+static int lookup_head_remote(git_buf *url, git_repository *repo)
{
- git_strmap *smcfg = repo->submodules;
- git_submodule *sm;
+ int error;
+ git_config *cfg;
+ git_reference *head = NULL, *remote = NULL;
+ const char *tgt, *scan;
+ git_buf key = GIT_BUF_INIT;
+
+ /* 1. resolve HEAD -> refs/heads/BRANCH
+ * 2. lookup config branch.BRANCH.remote -> ORIGIN
+ * 3. lookup remote.ORIGIN.url
+ */
- repo->submodules = NULL;
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
- if (smcfg == NULL)
- return;
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
+ giterr_set(GITERR_SUBMODULE,
+ "Cannot resolve relative URL when HEAD cannot be resolved");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
- git_strmap_foreach_value(smcfg, sm, {
- submodule_release(sm,1);
- });
- git_strmap_free(smcfg);
-}
+ if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
+ giterr_set(GITERR_SUBMODULE,
+ "Cannot resolve relative URL when HEAD is not symbolic");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
-static int submodule_cmp(const void *a, const void *b)
-{
- return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+ if ((error = git_branch_tracking(&remote, head)) < 0)
+ goto cleanup;
+
+ /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
+
+ if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
+ git__prefixcmp(git_reference_target(remote), "refs/remotes/") != 0)
+ {
+ giterr_set(GITERR_SUBMODULE,
+ "Cannot resolve relative URL when HEAD is not symbolic");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ scan = tgt = git_reference_target(remote) + strlen("refs/remotes/");
+ while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
+ scan++; /* find non-escaped slash to end ORIGIN name */
+
+ error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
+ goto cleanup;
+
+ error = git_buf_sets(url, tgt);
+
+cleanup:
+ git_buf_free(&key);
+ git_reference_free(head);
+ git_reference_free(remote);
+
+ return error;
}
-int git_submodule_foreach(
- git_repository *repo,
- int (*callback)(const char *name, void *payload),
- void *payload)
+static int submodule_update_config(
+ git_submodule *submodule,
+ const char *attr,
+ const char *value,
+ bool overwrite,
+ bool only_existing)
{
int error;
- git_submodule *sm;
- git_vector seen = GIT_VECTOR_INIT;
- seen._cmp = submodule_cmp;
+ git_config *config;
+ git_buf key = GIT_BUF_INIT;
+ const char *old = NULL;
+
+ assert(submodule);
- if ((error = load_submodule_config(repo)) < 0)
+ error = git_repository_config__weakptr(&config, submodule->owner);
+ if (error < 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;
- }
+ error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
+ if (error < 0)
+ goto cleanup;
- if ((error = callback(sm->name, payload)) < 0)
- break;
- });
+ if (git_config_get_string(&old, config, key.ptr) < 0)
+ giterr_clear();
- git_vector_free(&seen);
+ if (!old && only_existing)
+ goto cleanup;
+ if (old && !overwrite)
+ goto cleanup;
+ if ((!old && !value) || (old && value && strcmp(old, value) == 0))
+ goto cleanup;
+
+ if (!value)
+ error = git_config_delete(config, key.ptr);
+ else
+ error = git_config_set_string(config, key.ptr, value);
+cleanup:
+ git_buf_free(&key);
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 */
+static int submodule_index_status(unsigned int *status, git_submodule *sm)
{
- khiter_t pos;
+ const git_oid *head_oid = git_submodule_head_oid(sm);
+ const git_oid *index_oid = git_submodule_index_oid(sm);
- if (load_submodule_config(repo) < 0)
- return -1;
+ if (!head_oid) {
+ if (index_oid)
+ *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
+ }
+ else if (!index_oid)
+ *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
+ else if (!git_oid_equal(head_oid, index_oid))
+ *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
- pos = git_strmap_lookup_index(repo->submodules, name);
- if (!git_strmap_valid_index(repo->submodules, pos))
- return GIT_ENOTFOUND;
+ return 0;
+}
- if (sm_ptr)
- *sm_ptr = git_strmap_value_at(repo->submodules, pos);
+static int submodule_wd_status(unsigned int *status, git_submodule *sm)
+{
+ int error = 0;
+ const git_oid *wd_oid, *index_oid;
+ git_repository *sm_repo = NULL;
+
+ /* open repo now if we need it (so wd_oid() call won't reopen) */
+ if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
+ sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
+ (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
+ {
+ if ((error = git_submodule_open(&sm_repo, sm)) < 0)
+ return error;
+ }
- return 0;
+ index_oid = git_submodule_index_oid(sm);
+ wd_oid = git_submodule_wd_oid(sm);
+
+ if (!index_oid) {
+ if (wd_oid)
+ *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
+ }
+ else if (!wd_oid) {
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
+ (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
+ else
+ *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
+ }
+ else if (!git_oid_equal(index_oid, wd_oid))
+ *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
+
+ if (sm_repo != NULL) {
+ git_tree *sm_head;
+ git_diff_options opt;
+ git_diff_list *diff;
+
+ /* the diffs below could be optimized with an early termination
+ * option to the git_diff functions, but for now this is sufficient
+ * (and certainly no worse that what core git does).
+ */
+
+ /* perform head-to-index diff on submodule */
+
+ if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
+ return error;
+
+ memset(&opt, 0, sizeof(opt));
+ if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
+ opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+ error = git_diff_index_to_tree(sm_repo, &opt, sm_head, &diff);
+
+ if (!error) {
+ if (git_diff_entrycount(diff, -1) > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(sm_head);
+
+ if (error < 0)
+ return error;
+
+ /* perform index-to-workdir diff on submodule */
+
+ error = git_diff_workdir_to_index(sm_repo, &opt, &diff);
+
+ if (!error) {
+ int untracked = git_diff_entrycount(diff, GIT_DELTA_UNTRACKED);
+
+ if (untracked > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
+
+ if (git_diff_entrycount(diff, -1) - untracked > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ }
+
+ git_repository_free(sm_repo);
+ }
+
+ return error;
}
diff --git a/src/submodule.h b/src/submodule.h
new file mode 100644
index 00000000..c7a6aaf7
--- /dev/null
+++ b/src/submodule.h
@@ -0,0 +1,102 @@
+/*
+ * 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_submodule_h__
+#define INCLUDE_submodule_h__
+
+/* Notes:
+ *
+ * Submodule information can be in four places: the index, the config files
+ * (both .git/config and .gitmodules), the HEAD tree, and the working
+ * directory.
+ *
+ * In the index:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the HEAD tree:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the config files:
+ * - submodule is found by submodule "name" which is usually the path
+ * - may be missing or present
+ * - will have a name, path, url, and other properties
+ *
+ * In the working directory:
+ * - submodule is found by path
+ * - may be missing, an empty directory, a checked out directory,
+ * or of the wrong type
+ * - if checked out, will have a HEAD oid
+ * - if checked out, will have git history that can be used to compare oids
+ * - if checked out, may have modified files and/or untracked files
+ */
+
+/**
+ * Description of submodule
+ *
+ * This record describes a submodule found in a repository. There should be
+ * an entry for every submodule found in the HEAD and index, and for every
+ * submodule described in .gitmodules. The fields are as follows:
+ *
+ * - `owner` is the git_repository containing this submodule
+ * - `name` is the name of the submodule from .gitmodules.
+ * - `path` is the path to the submodule from the repo root. It is almost
+ * always the same as `name`.
+ * - `url` is the url for the submodule.
+ * - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
+ * - `index_oid` is the SHA1 for the submodule recorded in the index.
+ * - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
+ * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
+ * - `refcount` tracks how many hashmap entries there are for this submodule.
+ * It only comes into play if the name and path of the submodule differ.
+ * - `flags` is for internal use, tracking where this submodule has been
+ * found (head, index, config, workdir) and other misc info about it.
+ *
+ * If the submodule has been added to .gitmodules but not yet git added,
+ * then the `index_oid` will be valid and zero. If the submodule has been
+ * deleted, but the delete has not been committed yet, then the `index_oid`
+ * will be set, but the `url` will be NULL.
+ */
+struct git_submodule {
+ git_repository *owner;
+ char *name;
+ char *path; /* important: may point to same string data as "name" */
+ char *url;
+ uint32_t flags;
+ git_oid head_oid;
+ git_oid index_oid;
+ git_oid wd_oid;
+ /* information from config */
+ git_submodule_update_t update;
+ git_submodule_update_t update_default;
+ git_submodule_ignore_t ignore;
+ git_submodule_ignore_t ignore_default;
+ int fetch_recurse;
+ /* internal information */
+ int refcount;
+};
+
+/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
+enum {
+ GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID = (1u << 21),
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID = (1u << 22),
+ GIT_SUBMODULE_STATUS__WD_OID_VALID = (1u << 23),
+ GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24),
+ GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25),
+ GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26),
+ GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
+};
+
+#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
+ ((S) & ~(0xFFFFFFFFu << 20))
+
+#endif
diff --git a/src/transport.h b/src/transport.h
index c4306165..ff3a58d1 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -21,11 +21,15 @@
#define GIT_CAP_OFS_DELTA "ofs-delta"
#define GIT_CAP_MULTI_ACK "multi_ack"
+#define GIT_CAP_SIDE_BAND "side-band"
+#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
typedef struct git_transport_caps {
int common:1,
ofs_delta:1,
- multi_ack: 1;
+ multi_ack: 1,
+ side_band:1,
+ side_band_64k:1;
} git_transport_caps;
#ifdef GIT_SSL
@@ -84,6 +88,7 @@ struct git_transport {
gitno_buffer buffer;
GIT_SOCKET socket;
git_transport_caps caps;
+ void *cb_data;
/**
* Connect and store the remote heads
*/
@@ -113,6 +118,11 @@ struct git_transport {
* Free the associated resources
*/
void (*free)(struct git_transport *transport);
+ /**
+ * Callbacks for the progress and error output
+ */
+ void (*progress_cb)(const char *str, int len, void *data);
+ void (*error_cb)(const char *str, int len, void *data);
};
diff --git a/src/transports/git.c b/src/transports/git.c
index 7a65718f..b757495c 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -24,7 +24,7 @@
typedef struct {
git_transport parent;
- char buff[1024];
+ char buff[65536];
#ifdef GIT_WIN32
WSADATA wsd;
#endif
diff --git a/src/transports/http.c b/src/transports/http.c
index 85fec413..ce382c3a 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -233,7 +233,7 @@ static int http_recv_cb(gitno_buffer *buf)
if (t->error < 0)
return t->error;
- return buf->offset - old_len;
+ return (int)(buf->offset - old_len);
}
/* Set up the gitno_buffer so calling gitno_recv() grabs data from the HTTP response */
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 7a3a388e..25038c82 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -18,8 +18,8 @@
#define p_lstat(p,b) lstat(p,b)
#define p_readlink(a, b, c) readlink(a, b, c)
+#define p_symlink(o,n) symlink(o, n)
#define p_link(o,n) link(o, n)
-#define p_symlink(o,n) symlink(o,n)
#define p_unlink(p) unlink(p)
#define p_mkdir(p,m) mkdir(p, m)
#define p_fsync(fd) fsync(fd)
diff --git a/tests-clar/checkout/checkout.c b/tests-clar/checkout/checkout.c
index d6b79b4a..ba14194c 100644
--- a/tests-clar/checkout/checkout.c
+++ b/tests-clar/checkout/checkout.c
@@ -33,7 +33,7 @@ static void test_file_contents(const char *path, const char *expectedcontents)
actuallen = p_read(fd, buffer, 1024);
cl_git_pass(p_close(fd));
- cl_assert_equal_i(actuallen, expectedlen);
+ cl_assert_equal_sz(actuallen, expectedlen);
cl_assert_equal_s(buffer, expectedcontents);
}
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
index eab6c3d3..b4ee74cd 100644
--- a/tests-clar/clar_libgit2.h
+++ b/tests-clar/clar_libgit2.h
@@ -25,6 +25,8 @@
*/
#define cl_git_fail(expr) cl_must_fail(expr)
+#define cl_assert_equal_sz(sz1,sz2) cl_assert((sz1) == (sz2))
+
/*
* Some utility macros for building long strings
*/
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index b6274b01..972567e5 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -665,7 +665,7 @@ static void assert_unescape(char *expected, char *to_unescape) {
cl_git_pass(git_buf_sets(&buf, to_unescape));
git_buf_unescape(&buf);
cl_assert_equal_s(expected, buf.ptr);
- cl_assert_equal_i(strlen(expected), buf.size);
+ cl_assert_equal_sz(strlen(expected), buf.size);
git_buf_free(&buf);
}
diff --git a/tests-clar/core/copy.c b/tests-clar/core/copy.c
new file mode 100644
index 00000000..2fdfed86
--- /dev/null
+++ b/tests-clar/core/copy.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+void test_core_copy__file(void)
+{
+ struct stat st;
+ const char *content = "This is some stuff to copy\n";
+
+ cl_git_mkfile("copy_me", content);
+
+ cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664));
+
+ cl_git_pass(git_path_lstat("copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(p_unlink("copy_me_two"));
+ cl_git_pass(p_unlink("copy_me"));
+}
+
+void test_core_copy__file_in_dir(void)
+{
+ struct stat st;
+ const char *content = "This is some other stuff to copy\n";
+
+ cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
+ cl_assert(git_path_isdir("an_dir"));
+
+ cl_git_pass(git_futils_mkpath2file
+ ("an_dir/second_dir/and_more/copy_me_two", 0775));
+
+ cl_git_pass(git_futils_cp
+ ("an_dir/in_a_dir/copy_me",
+ "an_dir/second_dir/and_more/copy_me_two",
+ 0664));
+
+ cl_git_pass(git_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(git_futils_rmdir_r("an_dir", GIT_DIRREMOVAL_FILES_AND_DIRS));
+ cl_assert(!git_path_isdir("an_dir"));
+}
+
+void test_core_copy__tree(void)
+{
+ struct stat st;
+ const char *content = "File content\n";
+
+ cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH));
+
+ cl_git_mkfile("src/f1", content);
+ cl_git_mkfile("src/b/f2", content);
+ cl_git_mkfile("src/c/f3", content);
+ cl_git_mkfile("src/c/d/f4", content);
+ cl_git_mkfile("src/c/d/.f5", content);
+
+#ifndef GIT_WIN32
+ cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0);
+#endif
+
+ cl_assert(git_path_isdir("src"));
+ cl_assert(git_path_isdir("src/b"));
+ cl_assert(git_path_isdir("src/c/d"));
+ cl_assert(git_path_isfile("src/c/d/f4"));
+
+ /* copy with no empty dirs, yes links, no dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) );
+
+ cl_assert(git_path_isdir("t1"));
+ cl_assert(git_path_isdir("t1/b"));
+ cl_assert(git_path_isdir("t1/c"));
+ cl_assert(git_path_isdir("t1/c/d"));
+ cl_assert(!git_path_isdir("t1/c/e"));
+
+ cl_assert(git_path_isfile("t1/f1"));
+ cl_assert(git_path_isfile("t1/b/f2"));
+ cl_assert(git_path_isfile("t1/c/f3"));
+ cl_assert(git_path_isfile("t1/c/d/f4"));
+ cl_assert(!git_path_isfile("t1/c/d/.f5"));
+
+ cl_git_pass(git_path_lstat("t1/c/f3", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+#ifndef GIT_WIN32
+ cl_git_pass(git_path_lstat("t1/c/d/l1", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t1", GIT_DIRREMOVAL_FILES_AND_DIRS));
+ cl_assert(!git_path_isdir("t1"));
+
+ /* copy with empty dirs, no links, yes dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) );
+
+ cl_assert(git_path_isdir("t2"));
+ cl_assert(git_path_isdir("t2/b"));
+ cl_assert(git_path_isdir("t2/c"));
+ cl_assert(git_path_isdir("t2/c/d"));
+ cl_assert(git_path_isdir("t2/c/e"));
+
+ cl_assert(git_path_isfile("t2/f1"));
+ cl_assert(git_path_isfile("t2/b/f2"));
+ cl_assert(git_path_isfile("t2/c/f3"));
+ cl_assert(git_path_isfile("t2/c/d/f4"));
+ cl_assert(git_path_isfile("t2/c/d/.f5"));
+
+#ifndef GIT_WIN32
+ cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t2", GIT_DIRREMOVAL_FILES_AND_DIRS));
+ cl_assert(!git_path_isdir("t2"));
+
+ cl_git_pass(git_futils_rmdir_r("src", GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
diff --git a/tests-clar/core/mkdir.c b/tests-clar/core/mkdir.c
new file mode 100644
index 00000000..08ba2419
--- /dev/null
+++ b/tests-clar/core/mkdir.c
@@ -0,0 +1,182 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+static void cleanup_basic_dirs(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("d0", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d1", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d2", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d3", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d4", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__basic(void)
+{
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
+ cl_assert(git_path_isdir("d0"));
+
+ /* make a path */
+ cl_assert(!git_path_isdir("d1"));
+ cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d1"));
+ cl_assert(git_path_isdir("d1/d1.1"));
+ cl_assert(git_path_isdir("d1/d1.1/d1.2"));
+
+ /* make a dir exclusively */
+ cl_assert(!git_path_isdir("d2"));
+ cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d2"));
+
+ /* make exclusive failure */
+ cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+
+ /* make a path exclusively */
+ cl_assert(!git_path_isdir("d3"));
+ cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d3"));
+ cl_assert(git_path_isdir("d3/d3.1/d3.2"));
+
+ /* make exclusive path failure */
+ cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ /* ??? Should EXCL only apply to the last item in the path? */
+
+ /* path with trailing slash? */
+ cl_assert(!git_path_isdir("d4"));
+ cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d4/d4.1"));
+}
+
+static void cleanup_basedir(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("base", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__with_base(void)
+{
+#define BASEDIR "base/dir/here"
+
+ cl_set_cleanup(cleanup_basedir, NULL);
+
+ cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
+ cl_assert(git_path_isdir(BASEDIR "/a"));
+
+ cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
+
+ /* exclusive with existing base */
+ cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with duplicated suffix */
+ cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with any duplicated component */
+ cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* success: exclusive without path */
+ cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
+
+ /* path with shorter base and existing dirs */
+ cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("base/dir/here/d"));
+
+ /* fail: path with shorter base and existing dirs */
+ cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: base with missing components */
+ cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
+
+ /* success: shift missing component to path */
+ cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
+}
+
+static void cleanup_chmod_root(void *ref)
+{
+ mode_t *mode = ref;
+
+ if (*mode != 0) {
+ (void)p_umask(*mode);
+ git__free(mode);
+ }
+
+ git_futils_rmdir_r("r", GIT_DIRREMOVAL_EMPTY_HIERARCHY);
+}
+
+static void check_mode(mode_t expected, mode_t actual)
+{
+#ifdef GIT_WIN32
+ /* chmod on Win32 doesn't support exec bit, not group/world bits */
+ cl_assert((expected & 0600) == (actual & 0777));
+#else
+ cl_assert(expected == (actual & 0777));
+#endif
+}
+
+void test_core_mkdir__chmods(void)
+{
+ struct stat st;
+ mode_t *old = git__malloc(sizeof(mode_t));
+ *old = p_umask(022);
+
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
+ check_mode(0777, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod existing dir */
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod even existing dirs if CHMOD_PATH is set */
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
+ check_mode(0777, st.st_mode);
+}
diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c
index ac3cc005..f92bf486 100644
--- a/tests-clar/refs/list.c
+++ b/tests-clar/refs/list.c
@@ -36,7 +36,7 @@ void test_refs_list__all(void)
/* 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_equal_i(ref_list.count, 10);
+ cl_assert_equal_i((int)ref_list.count, 10);
git_strarray_free(&ref_list);
}
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
index 3d37c375..67a9917d 100644
--- a/tests-clar/repo/init.c
+++ b/tests-clar/repo/init.c
@@ -2,6 +2,7 @@
#include "fileops.h"
#include "repository.h"
#include "config.h"
+#include "path.h"
enum repo_mode {
STANDARD_REPOSITORY = 0,
@@ -29,6 +30,8 @@ static void ensure_repository_init(
{
const char *workdir;
+ cl_assert(!git_path_isdir(working_directory));
+
cl_git_pass(git_repository_init(&_repo, working_directory, is_bare));
workdir = git_repository_workdir(_repo);
@@ -46,7 +49,8 @@ static void ensure_repository_init(
#ifdef GIT_WIN32
if (!is_bare) {
- cl_assert((GetFileAttributes(git_repository_path(_repo)) & FILE_ATTRIBUTE_HIDDEN) != 0);
+ DWORD fattrs = GetFileAttributes(git_repository_path(_repo));
+ cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
}
#endif
@@ -83,7 +87,7 @@ void test_repo_init__bare_repo_escaping_current_workdir(void)
git_buf path_current_workdir = GIT_BUF_INIT;
cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
-
+
cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
@@ -295,3 +299,82 @@ void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
git_repository_free(_repo);
assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}
+
+void test_repo_init__extended_0(void)
+{
+ git_repository_init_options opts;
+ memset(&opts, 0, sizeof(opts));
+
+ /* without MKDIR this should fail */
+ cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+ cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ cleanup_repository("extended");
+}
+
+void test_repo_init__extended_1(void)
+{
+ git_reference *ref;
+ git_remote *remote;
+ struct stat st;
+ git_repository_init_options opts;
+ memset(&opts, 0, sizeof(opts));
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+ opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ opts.workdir_path = "../c_wd";
+ opts.description = "Awesomest test repository evah";
+ opts.initial_head = "development";
+ opts.origin_url = "https://github.com/libgit2/libgit2.git";
+
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
+ cl_assert(git_path_isfile("root/b/c_wd/.git"));
+ cl_assert(!git_repository_is_bare(_repo));
+ /* repo will not be counted as empty because we set head to "development" */
+ cl_assert(!git_repository_is_empty(_repo));
+
+ cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
+ cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
+ cl_assert_equal_s("refs/heads/development", git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_load(&remote, _repo, "origin"));
+ cl_assert_equal_s("origin", git_remote_name(remote));
+ cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
+ git_remote_free(remote);
+
+ git_repository_free(_repo);
+ cl_fixture_cleanup("root");
+}
+
+void test_repo_init__extended_with_template(void)
+{
+ git_repository_init_options opts;
+ memset(&opts, 0, sizeof(opts));
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE;
+ opts.template_path = cl_fixture("template");
+
+ cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));
+
+ cl_assert(git_repository_is_bare(_repo));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));
+
+ cleanup_repository("templated.git");
+}
diff --git a/tests-clar/resources/submod2/.gitted/HEAD b/tests-clar/resources/submod2/.gitted/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/config b/tests-clar/resources/submod2/.gitted/config
new file mode 100644
index 00000000..abc42073
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/config
@@ -0,0 +1,20 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "sm_missing_commits"]
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ url = ../submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/description b/tests-clar/resources/submod2/.gitted/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/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/submod2/.gitted/index b/tests-clar/resources/submod2/.gitted/index
new file mode 100644
index 00000000..0c17e862
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/info/exclude b/tests-clar/resources/submod2/.gitted/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/logs/HEAD b/tests-clar/resources/submod2/.gitted/logs/HEAD
new file mode 100644
index 00000000..2cf2ca74
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests-clar/resources/submod2/.gitted/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/logs/refs/heads/master
new file mode 100644
index 00000000..2cf2ca74
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
new file mode 100644
index 00000000..2d0583e9
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_added_and_uncommited
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/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/submod2/.gitted/modules/sm_added_and_uncommited/index b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
new file mode 100644
index 00000000..65140a51
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
new file mode 100644
index 00000000..53753e7d
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
new file mode 100644
index 00000000..53753e7d
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..53753e7d
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config
new file mode 100644
index 00000000..10cc2508
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/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/submod2/.gitted/modules/sm_changed_file/index b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index
new file mode 100644
index 00000000..6914a3b6
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
new file mode 100644
index 00000000..e5cb63f8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
new file mode 100644
index 00000000..e5cb63f8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..e5cb63f8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
new file mode 100644
index 00000000..6b8d1e3f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Making a change in a submodule
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config
new file mode 100644
index 00000000..7d002536
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_head
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/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/submod2/.gitted/modules/sm_changed_head/index b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index
new file mode 100644
index 00000000..728fa292
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
new file mode 100644
index 00000000..cabdeb2b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
new file mode 100644
index 00000000..cabdeb2b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..257ca21d
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
new file mode 100644
index 00000000..a2c37164
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
@@ -0,0 +1,3 @@
+x•ŽKj!E3vµ„jµüÀ#<Þ<“ì@­êéO°uÿq ™.çÂ)×ql ´‰o­Š€÷sFa#Èv‰ÓÅ )g#{':ªßTål`b¤4ë0 ;ïf¡ár‘4
+Ùä™
+ªÔÛzUøî÷-û/Ùg©ð¨ù¹lmíù£\Ç'LÆjrhÍïèÕXG_êŸê+ýlç ÊšÎE`;ß=÷]ÔÞJç \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
new file mode 100644
index 00000000..f8a236f3
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
new file mode 100644
index 00000000..8155b3e8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
@@ -0,0 +1,2 @@
+xMM;
+1µÎ)Þ ÁZPÐÞÆr²3kÉ l²En¿ƒl!¼æýc±ˆóõrz§Üà ,¹º¡çe +ÚlEZxuPY…x QC³*ðf·uLácfR3ŠÍT0'Ò¯øjƒŠ°ð~G¦^s1Šèb2z’ƒÿùVkî]Ü5<·ûv¨'>ã \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
new file mode 100644
index 00000000..ae079bd7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
@@ -0,0 +1 @@
+3d9386c507f6b093471a3e324085657a3c2b4247
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config
new file mode 100644
index 00000000..0274ff7e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_index
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/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/submod2/.gitted/modules/sm_changed_index/index b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index
new file mode 100644
index 00000000..6fad3b43
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
new file mode 100644
index 00000000..80eb5410
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
new file mode 100644
index 00000000..80eb5410
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..80eb5410
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
new file mode 100644
index 00000000..cb3f5a00
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
new file mode 100644
index 00000000..7f258447
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_untracked_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/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/submod2/.gitted/modules/sm_changed_untracked_file/index b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
new file mode 100644
index 00000000..598e30a3
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
new file mode 100644
index 00000000..d1beafbd
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
new file mode 100644
index 00000000..d1beafbd
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..d1beafbd
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config
new file mode 100644
index 00000000..45fbb30c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_missing_commits
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/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/submod2/.gitted/modules/sm_missing_commits/index b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index
new file mode 100644
index 00000000..49035652
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
new file mode 100644
index 00000000..ee08c970
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
new file mode 100644
index 00000000..ee08c970
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..ee08c970
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
new file mode 100644
index 00000000..66fbf5da
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+5e4963595a9774b90524d35a807169049de8ccad refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
new file mode 100644
index 00000000..3913aca5
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
@@ -0,0 +1 @@
+5e4963595a9774b90524d35a807169049de8ccad
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config
new file mode 100644
index 00000000..fc706c9d
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_unchanged
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/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/submod2/.gitted/modules/sm_unchanged/index b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index
new file mode 100644
index 00000000..629c849e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
new file mode 100644
index 00000000..72653286
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
new file mode 100644
index 00000000..72653286
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..72653286
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
new file mode 100644
index 00000000..5a4ebc47
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
new file mode 100644
index 00000000..6efe28ff
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e b/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
new file mode 100644
index 00000000..f1ea5f4c
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 b/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
new file mode 100644
index 00000000..d3c8582e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
@@ -0,0 +1 @@
+x•M F]sŠ¹€fh¡ccŒ;·Þ€ŸÁ’@I(Ü_OàöË{ïs%çØ@’>µÊ ^!¹²F'½‘!諲l£_¼q4Íä´ÇE˜Þ¶Rá݃S‚'§ÀnÕ>>±mÝ^\Éw³š´^‰$œ‘ÅXÇ_迦xí±E“à—_.à9} \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 b/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
new file mode 100644
index 00000000..fce6a94b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
@@ -0,0 +1,4 @@
+xµË
+Â0Eݶ_Qº·.
+.ü W"!1 æ!3 øù>+¶Š.¤Û9Ã=3Wº(«nÕ-¶”¥:;¨jòÜ[" WÑ{›¨Þ•ÅQ¤¾ZWï°,2º iviyh •“ÐT/‚=Ž{Ž‡ ¶!@b(¡bÎJcSËP¢¥rÅŒ
+è‡ð¡ã{ë`ì|%³imÐpú콡ÙÄ=ˆIÇÿW2›6‡„B@)|¼óÿ)g£ý™ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 b/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
new file mode 100644
index 00000000..2965becf
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad b/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
new file mode 100644
index 00000000..08faf0fa
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 b/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
new file mode 100644
index 00000000..ee7848ae
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 b/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
new file mode 100644
index 00000000..ca9203a6
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b b/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
new file mode 100644
index 00000000..9f88f6bd
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
@@ -0,0 +1,2 @@
+x•ŽÛ 1EýNÓ€2yg@D,A°€$;YöE6éßmÁß{Λ·e™(å/­2ƒõdƒ#ÊjÈšL 2—ìYdÊ:fÊž ˆ=V^D’hR Ä$¥^ÃÅ©ÉaŠÆ+tn {ûnÞý8xžáÅsá
+÷šžãÔ¾=Ýò¶<@j£¬CÔ®èŹžÿÚ©þ[ŠÏ>Ä6­#=-ÛÐg?,¯FŒ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d b/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
new file mode 100644
index 00000000..30bee40e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 b/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
new file mode 100644
index 00000000..79018042
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 b/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
new file mode 100644
index 00000000..cde89e5b
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 b/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
new file mode 100644
index 00000000..41af98aa
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 b/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
new file mode 100644
index 00000000..160f1caf
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
@@ -0,0 +1,2 @@
+xµË
+Â0Eݶ_º·Bqåg¸ yŒi ™IÀÏ÷Y±Up!ÝÎs¸£|R¬ï7«=’)XCAGä¢:…à25‡º:É<°-û„uUÐ_IÛò‡¤Y¢…\Ϥ%êAF fª{Gß qTœPsï”u¹ã(ÓZ{‰RA ô#ø̉£ó0m¾“Ų.8ïÞÑbáäìÇãÞù?{vÊŒ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 b/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
new file mode 100644
index 00000000..1ee52218
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 b/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
new file mode 100644
index 00000000..2239e14a
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb b/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
new file mode 100644
index 00000000..a03ea66e
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 b/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
new file mode 100644
index 00000000..292303eb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 b/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
new file mode 100644
index 00000000..b92c7eeb
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
@@ -0,0 +1,2 @@
+x•ÏÛ
+!€ánק}€ "‚.z’uRÉCx€}üΑۼøt¸ œ.׫Ù6î‚,iŸs&%ãÁ9“S¿#ݲ¦úIW¢=—a˜ßËf2A‹¼BYsÏñßÐa{c±¶^K3g¼Äñ³wMÍ F˜Üúøߥ4sÅçâ€òÇáõÎ÷'Nê°I \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c b/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
new file mode 100644
index 00000000..3c7750b1
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 b/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
new file mode 100644
index 00000000..219620b2
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
@@ -0,0 +1,2 @@
+xeÍÁ
+Â0„a¯íS„ÞíbOzð1<I Iº¤¤‘Íû+ˆ‚õ:?|ãsõæt9îh¾Ô¥e6Š- H[´¡–’ÃÜw§«¹šÿØwMò«Œ#½‘ɪ“ÈÚïж…Õm‘—_î; º$ž rò1éDÊPCvB¨Mcø‡ýI^ \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b b/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
new file mode 100644
index 00000000..883a40bf
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
@@ -0,0 +1 @@
+xEŒA€ =óŠý„ÿáZ)¤RE¿/ñb2·É «1¶uÙsé˜xôÁ§Å¡—îˆä>ßä2<E™nG=2,ýÉœTÄ’’4©Žî4!¼N¬$` \ No newline at end of file
diff --git a/tests-clar/resources/submod2/.gitted/refs/heads/master b/tests-clar/resources/submod2/.gitted/refs/heads/master
new file mode 100644
index 00000000..d1d38aa4
--- /dev/null
+++ b/tests-clar/resources/submod2/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7484482eb8db738cafa696993664607500a3f2b9
diff --git a/tests-clar/resources/submod2/README.txt b/tests-clar/resources/submod2/README.txt
new file mode 100644
index 00000000..f990a25a
--- /dev/null
+++ b/tests-clar/resources/submod2/README.txt
@@ -0,0 +1,3 @@
+This is the submodule test data
+This repo will have a bunch of submodules in different states
+
diff --git a/tests-clar/resources/submod2/gitmodules b/tests-clar/resources/submod2/gitmodules
new file mode 100644
index 00000000..7b150b18
--- /dev/null
+++ b/tests-clar/resources/submod2/gitmodules
@@ -0,0 +1,21 @@
+[submodule "sm_missing_commits"]
+ path = sm_missing_commits
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ path = sm_unchanged
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ path = sm_changed_file
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ path = sm_changed_index
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ path = sm_changed_head
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ path = sm_changed_untracked_file
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ path = sm_added_and_uncommited
+ url = ../submod2_target
diff --git a/tests-clar/resources/submod2/just_a_dir/contents b/tests-clar/resources/submod2/just_a_dir/contents
new file mode 100644
index 00000000..7ba4c5c3
--- /dev/null
+++ b/tests-clar/resources/submod2/just_a_dir/contents
@@ -0,0 +1 @@
+This is a file in a plain directory
diff --git a/tests-clar/resources/submod2/just_a_file b/tests-clar/resources/submod2/just_a_file
new file mode 100644
index 00000000..42cfb95c
--- /dev/null
+++ b/tests-clar/resources/submod2/just_a_file
@@ -0,0 +1 @@
+This is just a plain file
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/COMMIT_EDITMSG b/tests-clar/resources/submod2/not_submodule/.gitted/COMMIT_EDITMSG
new file mode 100644
index 00000000..5852f446
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Initial commit
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/HEAD b/tests-clar/resources/submod2/not_submodule/.gitted/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/config b/tests-clar/resources/submod2/not_submodule/.gitted/config
new file mode 100644
index 00000000..af107929
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/description b/tests-clar/resources/submod2/not_submodule/.gitted/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/hooks/applypatch-msg.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/commit-msg.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/commit-msg.sample
new file mode 100755
index 00000000..b58d1184
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/post-update.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/post-update.sample
new file mode 100755
index 00000000..ec17ec19
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.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/submod2/not_submodule/.gitted/hooks/pre-applypatch.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-applypatch.sample
new file mode 100755
index 00000000..b1f187c2
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/pre-commit.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-commit.sample
new file mode 100755
index 00000000..18c48297
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/pre-rebase.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/pre-rebase.sample
new file mode 100755
index 00000000..9773ed4c
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/prepare-commit-msg.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/prepare-commit-msg.sample
new file mode 100755
index 00000000..f093a02e
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/hooks/update.sample b/tests-clar/resources/submod2/not_submodule/.gitted/hooks/update.sample
new file mode 100755
index 00000000..71ab04ed
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/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/submod2/not_submodule/.gitted/index b/tests-clar/resources/submod2/not_submodule/.gitted/index
new file mode 100644
index 00000000..f3fafa53
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/info/exclude b/tests-clar/resources/submod2/not_submodule/.gitted/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/logs/HEAD b/tests-clar/resources/submod2/not_submodule/.gitted/logs/HEAD
new file mode 100644
index 00000000..1749e7df
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/logs/refs/heads/master b/tests-clar/resources/submod2/not_submodule/.gitted/logs/refs/heads/master
new file mode 100644
index 00000000..1749e7df
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 b/tests-clar/resources/submod2/not_submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
new file mode 100644
index 00000000..8892531a
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
Binary files differ
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 b/tests-clar/resources/submod2/not_submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
new file mode 100644
index 00000000..c4e1a77d
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
Binary files differ
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e b/tests-clar/resources/submod2/not_submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
new file mode 100644
index 00000000..e9f1942a
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
Binary files differ
diff --git a/tests-clar/resources/submod2/not_submodule/.gitted/refs/heads/master b/tests-clar/resources/submod2/not_submodule/.gitted/refs/heads/master
new file mode 100644
index 00000000..0bd8514b
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/.gitted/refs/heads/master
@@ -0,0 +1 @@
+68e92c611b80ee1ed8f38314ff9577f0d15b2444
diff --git a/tests-clar/resources/submod2/not_submodule/README.txt b/tests-clar/resources/submod2/not_submodule/README.txt
new file mode 100644
index 00000000..71ff9927
--- /dev/null
+++ b/tests-clar/resources/submod2/not_submodule/README.txt
@@ -0,0 +1 @@
+This is a git repo but not a submodule
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted b/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted
new file mode 100644
index 00000000..2b2a4cf9
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_added_and_uncommited
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt b/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify b/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify
new file mode 100644
index 00000000..789efbda
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests-clar/resources/submod2/sm_changed_file/.gitted b/tests-clar/resources/submod2/sm_changed_file/.gitted
new file mode 100644
index 00000000..dc98b167
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_file
diff --git a/tests-clar/resources/submod2/sm_changed_file/README.txt b/tests-clar/resources/submod2/sm_changed_file/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_changed_file/file_to_modify b/tests-clar/resources/submod2/sm_changed_file/file_to_modify
new file mode 100644
index 00000000..e5ba6716
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_file/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+In this case, the file is changed in the workdir
diff --git a/tests-clar/resources/submod2/sm_changed_head/.gitted b/tests-clar/resources/submod2/sm_changed_head/.gitted
new file mode 100644
index 00000000..d5419b62
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_head/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_head
diff --git a/tests-clar/resources/submod2/sm_changed_head/README.txt b/tests-clar/resources/submod2/sm_changed_head/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_head/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_changed_head/file_to_modify b/tests-clar/resources/submod2/sm_changed_head/file_to_modify
new file mode 100644
index 00000000..8eb1e637
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_head/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+This one has been changed and the change has been committed to HEAD.
diff --git a/tests-clar/resources/submod2/sm_changed_index/.gitted b/tests-clar/resources/submod2/sm_changed_index/.gitted
new file mode 100644
index 00000000..2c7a5b27
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_index/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_index
diff --git a/tests-clar/resources/submod2/sm_changed_index/README.txt b/tests-clar/resources/submod2/sm_changed_index/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_index/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_changed_index/file_to_modify b/tests-clar/resources/submod2/sm_changed_index/file_to_modify
new file mode 100644
index 00000000..a02d3177
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_index/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+Here the file is changed in the index and the workdir
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted b/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted
new file mode 100644
index 00000000..9a107064
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_untracked_file
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt b/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify b/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify
new file mode 100644
index 00000000..789efbda
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked b/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked
new file mode 100644
index 00000000..d2bae616
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked
@@ -0,0 +1 @@
+This file is untracked, but in a submodule
diff --git a/tests-clar/resources/submod2/sm_missing_commits/.gitted b/tests-clar/resources/submod2/sm_missing_commits/.gitted
new file mode 100644
index 00000000..70193be8
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_missing_commits/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_missing_commits
diff --git a/tests-clar/resources/submod2/sm_missing_commits/README.txt b/tests-clar/resources/submod2/sm_missing_commits/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_missing_commits/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_missing_commits/file_to_modify b/tests-clar/resources/submod2/sm_missing_commits/file_to_modify
new file mode 100644
index 00000000..8834b635
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_missing_commits/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+
diff --git a/tests-clar/resources/submod2/sm_unchanged/.gitted b/tests-clar/resources/submod2/sm_unchanged/.gitted
new file mode 100644
index 00000000..51a679c8
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_unchanged/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_unchanged
diff --git a/tests-clar/resources/submod2/sm_unchanged/README.txt b/tests-clar/resources/submod2/sm_unchanged/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_unchanged/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2/sm_unchanged/file_to_modify b/tests-clar/resources/submod2/sm_unchanged/file_to_modify
new file mode 100644
index 00000000..789efbda
--- /dev/null
+++ b/tests-clar/resources/submod2/sm_unchanged/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests-clar/resources/submod2_target/.gitted/HEAD b/tests-clar/resources/submod2_target/.gitted/HEAD
new file mode 100644
index 00000000..cb089cd8
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submod2_target/.gitted/config b/tests-clar/resources/submod2_target/.gitted/config
new file mode 100644
index 00000000..af107929
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/submod2_target/.gitted/description b/tests-clar/resources/submod2_target/.gitted/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submod2_target/.gitted/hooks/applypatch-msg.sample b/tests-clar/resources/submod2_target/.gitted/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/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/submod2_target/.gitted/index b/tests-clar/resources/submod2_target/.gitted/index
new file mode 100644
index 00000000..eb3ff8c1
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/info/exclude b/tests-clar/resources/submod2_target/.gitted/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/HEAD b/tests-clar/resources/submod2_target/.gitted/logs/HEAD
new file mode 100644
index 00000000..0ecd1113
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master b/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master
new file mode 100644
index 00000000..0ecd1113
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 00000000..f4b7094c
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 00000000..56c845e4
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 00000000..bd179b5f
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 00000000..ccf49bd1
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 00000000..53029069
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 00000000..38c791eb
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 00000000..a26d2999
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 00000000..83d1ba48
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 00000000..6d27af8a
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 00000000..17458840
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 00000000..83cc29fb
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 00000000..55bda40e
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/refs/heads/master b/tests-clar/resources/submod2_target/.gitted/refs/heads/master
new file mode 100644
index 00000000..e12c44d7
--- /dev/null
+++ b/tests-clar/resources/submod2_target/.gitted/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests-clar/resources/submod2_target/README.txt b/tests-clar/resources/submod2_target/README.txt
new file mode 100644
index 00000000..780d7397
--- /dev/null
+++ b/tests-clar/resources/submod2_target/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests-clar/resources/submod2_target/file_to_modify b/tests-clar/resources/submod2_target/file_to_modify
new file mode 100644
index 00000000..789efbda
--- /dev/null
+++ b/tests-clar/resources/submod2_target/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests-clar/resources/template/branches/.gitignore b/tests-clar/resources/template/branches/.gitignore
new file mode 100644
index 00000000..16868ced
--- /dev/null
+++ b/tests-clar/resources/template/branches/.gitignore
@@ -0,0 +1,2 @@
+# This file should not be copied, nor should the
+# containing directory, since it is effectively "empty"
diff --git a/tests-clar/resources/template/description b/tests-clar/resources/template/description
new file mode 100644
index 00000000..498b267a
--- /dev/null
+++ b/tests-clar/resources/template/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/template/hooks/applypatch-msg.sample b/tests-clar/resources/template/hooks/applypatch-msg.sample
new file mode 100755
index 00000000..8b2a2fe8
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/commit-msg.sample b/tests-clar/resources/template/hooks/commit-msg.sample
new file mode 100755
index 00000000..b58d1184
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/post-commit.sample b/tests-clar/resources/template/hooks/post-commit.sample
new file mode 100755
index 00000000..22668216
--- /dev/null
+++ b/tests-clar/resources/template/hooks/post-commit.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script that is called after a successful
+# commit is made.
+#
+# To enable this hook, rename this file to "post-commit".
+
+: Nothing
diff --git a/tests-clar/resources/template/hooks/post-receive.sample b/tests-clar/resources/template/hooks/post-receive.sample
new file mode 100755
index 00000000..7a83e17a
--- /dev/null
+++ b/tests-clar/resources/template/hooks/post-receive.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script for the "post-receive" event.
+#
+# The "post-receive" script is run after receive-pack has accepted a pack
+# and the repository has been updated. It is passed arguments in through
+# stdin in the form
+# <oldrev> <newrev> <refname>
+# For example:
+# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+#
+# see contrib/hooks/ for a sample, or uncomment the next line and
+# rename the file to "post-receive".
+
+#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
diff --git a/tests-clar/resources/template/hooks/post-update.sample b/tests-clar/resources/template/hooks/post-update.sample
new file mode 100755
index 00000000..ec17ec19
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/pre-applypatch.sample b/tests-clar/resources/template/hooks/pre-applypatch.sample
new file mode 100755
index 00000000..b1f187c2
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/pre-commit.sample b/tests-clar/resources/template/hooks/pre-commit.sample
new file mode 100755
index 00000000..b187c4bb
--- /dev/null
+++ b/tests-clar/resources/template/hooks/pre-commit.sample
@@ -0,0 +1,46 @@
+#!/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)
+
+# 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')"
+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
+
+exec git diff-index --check --cached $against --
diff --git a/tests-clar/resources/template/hooks/pre-rebase.sample b/tests-clar/resources/template/hooks/pre-rebase.sample
new file mode 100755
index 00000000..9773ed4c
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/prepare-commit-msg.sample b/tests-clar/resources/template/hooks/prepare-commit-msg.sample
new file mode 100755
index 00000000..f093a02e
--- /dev/null
+++ b/tests-clar/resources/template/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/template/hooks/update.sample b/tests-clar/resources/template/hooks/update.sample
new file mode 100755
index 00000000..71ab04ed
--- /dev/null
+++ b/tests-clar/resources/template/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/template/info/exclude b/tests-clar/resources/template/info/exclude
new file mode 100644
index 00000000..a5196d1b
--- /dev/null
+++ b/tests-clar/resources/template/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index 9c6d7ee6..9092d515 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -199,3 +199,18 @@ void test_status_ignore__adding_internal_ignores(void)
cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
cl_assert(ignored);
}
+
+void test_status_ignore__add_internal_as_first_thing(void)
+{
+ int ignored;
+ const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, add_me));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.tmp"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+}
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
index 043b8300..85a7cd6b 100644
--- a/tests-clar/status/status_data.h
+++ b/tests-clar/status/status_data.h
@@ -44,7 +44,7 @@ static const unsigned int entry_statuses0[] = {
GIT_STATUS_WT_NEW,
};
-static const size_t entry_count0 = 16;
+static const int entry_count0 = 16;
/* entries for a copy of tests/resources/status with all content
* deleted from the working directory
@@ -86,7 +86,7 @@ static const unsigned int entry_statuses2[] = {
GIT_STATUS_WT_DELETED,
};
-static const size_t entry_count2 = 15;
+static const int entry_count2 = 15;
/* entries for a copy of tests/resources/status with some mods */
@@ -140,7 +140,7 @@ static const unsigned int entry_statuses3[] = {
GIT_STATUS_WT_NEW,
};
-static const size_t entry_count3 = 22;
+static const int entry_count3 = 22;
/* entries for a copy of tests/resources/status with some mods
@@ -199,4 +199,4 @@ static const unsigned int entry_statuses4[] = {
GIT_STATUS_WT_NEW,
};
-static const size_t entry_count4 = 23;
+static const int entry_count4 = 23;
diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h
index cffca66a..3f9c1f57 100644
--- a/tests-clar/status/status_helpers.h
+++ b/tests-clar/status/status_helpers.h
@@ -2,12 +2,12 @@
#define INCLUDE_cl_status_helpers_h__
typedef struct {
- size_t wrong_status_flags_count;
- size_t wrong_sorted_path;
- size_t entry_count;
+ int wrong_status_flags_count;
+ int wrong_sorted_path;
+ int entry_count;
const unsigned int* expected_statuses;
const char** expected_paths;
- size_t expected_entry_count;
+ int expected_entry_count;
} status_entry_counts;
/* cb_status__normal takes payload of "status_entry_counts *" */
diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c
index 9423e849..24dd660a 100644
--- a/tests-clar/status/submodules.c
+++ b/tests-clar/status/submodules.c
@@ -3,24 +3,17 @@
#include "path.h"
#include "posix.h"
#include "status_helpers.h"
+#include "../submodule/submodule_helpers.h"
static git_repository *g_repo = NULL;
void test_status_submodules__initialize(void)
{
- 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);
+ rewrite_gitmodules(git_repository_workdir(g_repo));
p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
}
@@ -28,6 +21,7 @@ void test_status_submodules__initialize(void)
void test_status_submodules__cleanup(void)
{
cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("testrepo.git");
}
void test_status_submodules__api(void)
@@ -40,8 +34,8 @@ void test_status_submodules__api(void)
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);
+ cl_assert_equal_s("testrepo", git_submodule_name(sm));
+ cl_assert_equal_s("testrepo", git_submodule_path(sm));
}
void test_status_submodules__0(void)
@@ -56,7 +50,7 @@ void test_status_submodules__0(void)
git_status_foreach(g_repo, cb_status__count, &counts)
);
- cl_assert(counts == 6);
+ cl_assert_equal_i(6, counts);
}
static const char *expected_files[] = {
@@ -101,12 +95,12 @@ void test_status_submodules__1(void)
git_status_foreach(g_repo, cb_status__match, &index)
);
- cl_assert(index == 6);
+ cl_assert_equal_i(6, index);
}
void test_status_submodules__single_file(void)
{
- unsigned int status;
+ unsigned int status = 0;
cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
- cl_assert(status == 0);
+ cl_assert(!status);
}
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index 2abf3683..75975c98 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -683,7 +683,7 @@ static unsigned int filemode_statuses[] = {
GIT_STATUS_WT_NEW
};
-static const size_t filemode_count = 8;
+static const int filemode_count = 8;
void test_status_worktree__filemode_changes(void)
{
@@ -697,7 +697,7 @@ void test_status_worktree__filemode_changes(void)
if (cl_is_chmod_supported())
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
else {
- unsigned int i;
+ int i;
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
/* won't trust filesystem mode diffs, so these will appear unchanged */
diff --git a/tests-clar/submodule/lookup.c b/tests-clar/submodule/lookup.c
new file mode 100644
index 00000000..669338f1
--- /dev/null
+++ b/tests-clar/submodule/lookup.c
@@ -0,0 +1,110 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "posix.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("submod2");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ /* must create submod2_target before rewrite so prettify will work */
+ rewrite_gitmodules(git_repository_workdir(g_repo));
+ p_rename("submod2/not_submodule/.gitted", "submod2/not_submodule/.git");
+}
+
+void test_submodule_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("submod2_target");
+}
+
+void test_submodule_lookup__simple_lookup(void)
+{
+ git_submodule *sm;
+
+ /* lookup existing */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is not in HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert(sm);
+
+ /* lookup git repo subdir that is not added as submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "not_submodule") == GIT_EEXISTS);
+
+ /* lookup existing directory that is not a submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND);
+
+ /* lookup existing file that is not a submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND);
+
+ /* lookup non-existent item */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND);
+}
+
+void test_submodule_lookup__accessors(void)
+{
+ git_submodule *sm;
+ const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(git_submodule_owner(sm) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+ cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_oid(sm), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_oid(sm),
+ "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_oid(sm), oid) == 0);
+ cl_assert(git_submodule_head_oid(sm) == NULL);
+ cl_assert(git_oid_streq(git_submodule_wd_oid(sm), oid) == 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_oid(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_oid(sm),
+ "5e4963595a9774b90524d35a807169049de8ccad") == 0);
+}
+
+typedef struct {
+ int count;
+} sm_lookup_data;
+
+static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
+{
+ sm_lookup_data *data = payload;
+ data->count += 1;
+ cl_assert_equal_s(git_submodule_name(sm), name);
+ return 0;
+}
+
+void test_submodule_lookup__foreach(void)
+{
+ sm_lookup_data data;
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(7, data.count);
+}
diff --git a/tests-clar/submodule/modify.c b/tests-clar/submodule/modify.c
new file mode 100644
index 00000000..ffbbe891
--- /dev/null
+++ b/tests-clar/submodule/modify.c
@@ -0,0 +1,257 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
+#define SM_LIBGIT2 "sm_libgit2"
+#define SM_LIBGIT2B "sm_libgit2b"
+
+void test_submodule_modify__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("submod2");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ /* must create submod2_target before rewrite so prettify will work */
+ rewrite_gitmodules(git_repository_workdir(g_repo));
+ p_rename("submod2/not_submodule/.gitted", "submod2/not_submodule/.git");
+}
+
+void test_submodule_modify__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("submod2_target");
+}
+
+void test_submodule_modify__add(void)
+{
+ git_submodule *sm;
+ git_config *cfg;
+ const char *s;
+
+ /* re-add existing submodule */
+ cl_assert(
+ git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1) ==
+ GIT_EEXISTS );
+
+ /* add a submodule using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1)
+ );
+
+ cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git"));
+
+ cl_assert(git_path_isdir("submod2/.git/modules"));
+ cl_assert(git_path_isdir("submod2/.git/modules/" SM_LIBGIT2));
+ cl_assert(git_path_isfile("submod2/.git/modules/" SM_LIBGIT2 "/HEAD"));
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2 ".url"));
+ cl_assert_equal_s(s, SM_LIBGIT2_URL);
+ git_config_free(cfg);
+
+ /* add a submodule not using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0)
+ );
+
+ cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git"));
+ cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD"));
+ cl_assert(!git_path_exists("submod2/.git/modules/" SM_LIBGIT2B));
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2B ".url"));
+ cl_assert_equal_s(s, SM_LIBGIT2_URL);
+ git_config_free(cfg);
+}
+
+static int delete_one_config(
+ const char *var_name, const char *value, void *payload)
+{
+ git_config *cfg = payload;
+ GIT_UNUSED(value);
+ return git_config_delete(cfg, var_name);
+}
+
+static int init_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_init(sm, false);
+}
+
+void test_submodule_modify__init(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ /* erase submodule data from .git/config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_foreach_match(cfg, "submodule\\..*", delete_one_config, cfg));
+ git_config_free(cfg);
+
+ /* confirm no submodule data in config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ git_config_free(cfg);
+
+ /* call init and see that settings are copied */
+ cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
+
+ git_submodule_reload_all(g_repo);
+
+ /* confirm submodule data in config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ git_config_free(cfg);
+}
+
+static int sync_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_sync(sm);
+}
+
+void test_submodule_modify__sync(void)
+{
+ git_submodule *sm1, *sm2, *sm3;
+ git_config *cfg;
+ const char *str;
+
+#define SM1 "sm_unchanged"
+#define SM2 "sm_changed_head"
+#define SM3 "sm_added_and_uncommited"
+
+ /* look up some submodules */
+ cl_git_pass(git_submodule_lookup(&sm1, g_repo, SM1));
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, SM2));
+ cl_git_pass(git_submodule_lookup(&sm3, g_repo, SM3));
+
+ /* At this point, the .git/config URLs for the submodules have
+ * not be rewritten with the absolute paths (although the
+ * .gitmodules have. Let's confirm that they DO NOT match
+ * yet, then we can do a sync to make them match...
+ */
+
+ /* check submodule info does not match before sync */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
+ cl_assert(strcmp(git_submodule_url(sm1), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
+ cl_assert(strcmp(git_submodule_url(sm2), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
+ cl_assert(strcmp(git_submodule_url(sm3), str) != 0);
+ git_config_free(cfg);
+
+ /* sync all the submodules */
+ cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL));
+
+ /* check that submodule config is updated */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
+ cl_assert_equal_s(git_submodule_url(sm1), str);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
+ cl_assert_equal_s(git_submodule_url(sm2), str);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
+ cl_assert_equal_s(git_submodule_url(sm3), str);
+ git_config_free(cfg);
+}
+
+void test_submodule_modify__edit_and_save(void)
+{
+ git_submodule *sm1, *sm2;
+ char *old_url;
+ git_submodule_ignore_t old_ignore;
+ git_submodule_update_t old_update;
+ git_repository *r2;
+
+ cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head"));
+
+ old_url = git__strdup(git_submodule_url(sm1));
+
+ /* modify properties of submodule */
+ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
+ old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
+ old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+
+ /* revert without saving (and confirm setters return old value) */
+ cl_git_pass(git_submodule_set_url(sm1, old_url));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED,
+ (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE,
+ (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT));
+
+ /* check that revert was successful */
+ cl_assert_equal_s(old_url, git_submodule_url(sm1));
+ cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i((int)old_update, (int)git_submodule_update(sm1));
+
+ /* modify properties of submodule (again) */
+ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
+ git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
+ git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
+
+ /* call save */
+ cl_git_pass(git_submodule_save(sm1));
+
+ /* attempt to "revert" values */
+ git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT);
+ git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT);
+
+ /* but ignore and update should NOT revert because the DEFAULT
+ * should now be the newly saved value...
+ */
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+
+ /* call reload and check that the new values are loaded */
+ cl_git_pass(git_submodule_reload(sm1));
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+
+ /* open a second copy of the repo and compare submodule */
+ cl_git_pass(git_repository_open(&r2, "submod2"));
+ cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head"));
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm2));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm2));
+
+ git_repository_free(r2);
+ git__free(old_url);
+}
diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c
new file mode 100644
index 00000000..d3a39235
--- /dev/null
+++ b/tests-clar/submodule/status.c
@@ -0,0 +1,308 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_status__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("submod2");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ /* must create submod2_target before rewrite so prettify will work */
+ rewrite_gitmodules(git_repository_workdir(g_repo));
+ p_rename("submod2/not_submodule/.gitted", "submod2/not_submodule/.git");
+}
+
+void test_submodule_status__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("submod2_target");
+}
+
+void test_submodule_status__unchanged(void)
+{
+ unsigned int status, expected;
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ expected = GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD;
+
+ cl_assert(status == expected);
+}
+
+/* 4 values of GIT_SUBMODULE_IGNORE to check */
+
+void test_submodule_status__ignore_none(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), GIT_DIRREMOVAL_FILES_AND_DIRS));
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "not_submodule"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ /* remove sm_changed_head from index */
+ {
+ git_index *index;
+ int pos;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ pos = git_index_find(index, "sm_changed_head");
+ cl_assert(pos >= 0);
+ cl_git_pass(git_index_remove(index, pos));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+ }
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
+
+ git_buf_free(&path);
+}
+
+static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
+{
+ git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload;
+ GIT_UNUSED(name);
+ git_submodule_set_ignore(sm, ignore);
+ return 0;
+}
+
+void test_submodule_status__ignore_untracked(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), GIT_DIRREMOVAL_FILES_AND_DIRS));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "not_submodule"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ git_buf_free(&path);
+}
+
+void test_submodule_status__ignore_dirty(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), GIT_DIRREMOVAL_FILES_AND_DIRS));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "not_submodule"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ git_buf_free(&path);
+}
+
+void test_submodule_status__ignore_all(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), GIT_DIRREMOVAL_FILES_AND_DIRS));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "not_submodule"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ git_buf_free(&path);
+}
diff --git a/tests-clar/submodule/submodule_helpers.c b/tests-clar/submodule/submodule_helpers.c
new file mode 100644
index 00000000..0c3e79f7
--- /dev/null
+++ b/tests-clar/submodule/submodule_helpers.c
@@ -0,0 +1,84 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "util.h"
+#include "posix.h"
+#include "submodule_helpers.h"
+
+/* rewrite gitmodules -> .gitmodules
+ * rewrite the empty or relative urls inside each module
+ * rename the .gitted directory inside any submodule to .git
+ */
+void rewrite_gitmodules(const char *workdir)
+{
+ git_buf in_f = GIT_BUF_INIT, out_f = GIT_BUF_INIT, path = GIT_BUF_INIT;
+ FILE *in, *out;
+ char line[256];
+
+ cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules"));
+ cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules"));
+
+ cl_assert((in = fopen(in_f.ptr, "r")) != NULL);
+ cl_assert((out = fopen(out_f.ptr, "w")) != NULL);
+
+ while (fgets(line, sizeof(line), in) != NULL) {
+ char *scan = line;
+
+ while (*scan == ' ' || *scan == '\t') scan++;
+
+ /* rename .gitted -> .git in submodule directories */
+ if (git__prefixcmp(scan, "path =") == 0) {
+ scan += strlen("path =");
+ while (*scan == ' ') scan++;
+
+ git_buf_joinpath(&path, workdir, scan);
+ git_buf_rtrim(&path);
+ git_buf_joinpath(&path, path.ptr, ".gitted");
+
+ if (!git_buf_oom(&path) && p_access(path.ptr, F_OK) == 0) {
+ git_buf_joinpath(&out_f, workdir, scan);
+ git_buf_rtrim(&out_f);
+ git_buf_joinpath(&out_f, out_f.ptr, ".git");
+
+ if (!git_buf_oom(&out_f))
+ p_rename(path.ptr, out_f.ptr);
+ }
+ }
+
+ /* copy non-"url =" lines verbatim */
+ if (git__prefixcmp(scan, "url =") != 0) {
+ fputs(line, out);
+ continue;
+ }
+
+ /* convert relative URLs in "url =" lines */
+ scan += strlen("url =");
+ while (*scan == ' ') scan++;
+
+ if (*scan == '.') {
+ git_buf_joinpath(&path, workdir, scan);
+ git_buf_rtrim(&path);
+ } else if (!*scan || *scan == '\n') {
+ git_buf_joinpath(&path, workdir, "../testrepo.git");
+ } else {
+ fputs(line, out);
+ continue;
+ }
+
+ git_path_prettify(&path, path.ptr, NULL);
+ git_buf_putc(&path, '\n');
+ cl_assert(!git_buf_oom(&path));
+
+ fwrite(line, scan - line, sizeof(char), out);
+ fputs(path.ptr, out);
+ }
+
+ fclose(in);
+ fclose(out);
+
+ cl_must_pass(p_unlink(in_f.ptr));
+
+ git_buf_free(&in_f);
+ git_buf_free(&out_f);
+ git_buf_free(&path);
+}
diff --git a/tests-clar/submodule/submodule_helpers.h b/tests-clar/submodule/submodule_helpers.h
new file mode 100644
index 00000000..6b76a832
--- /dev/null
+++ b/tests-clar/submodule/submodule_helpers.h
@@ -0,0 +1,2 @@
+extern void rewrite_gitmodules(const char *workdir);
+
diff --git a/tests-clar/valgrind-supp-mac.txt b/tests-clar/valgrind-supp-mac.txt
new file mode 100644
index 00000000..03e60dcd
--- /dev/null
+++ b/tests-clar/valgrind-supp-mac.txt
@@ -0,0 +1,82 @@
+{
+ libgit2-giterr-set-buffer
+ Memcheck:Leak
+ ...
+ fun:git__realloc
+ fun:git_buf_try_grow
+ fun:git_buf_grow
+ fun:git_buf_vprintf
+ fun:giterr_set
+}
+{
+ mac-setenv-leak-1
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:__setenv
+ fun:setenv
+}
+{
+ mac-setenv-leak-2
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:malloc_set_zone_name
+ ...
+ fun:init__zone0
+ fun:setenv
+}
+{
+ mac-dyld-initializer-leak
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dyld_register_image_state_change_handler
+ fun:_dyld_initializer
+}
+{
+ mac-tz-leak-1
+ Memcheck:Leak
+ ...
+ fun:token_table_add
+ fun:notify_register_check
+ fun:notify_register_tz
+}
+{
+ mac-tz-leak-2
+ Memcheck:Leak
+ fun:malloc
+ fun:tzload
+}
+{
+ mac-tz-leak-3
+ Memcheck:Leak
+ fun:malloc
+ fun:tzsetwall_basic
+}
+{
+ mac-tz-leak-4
+ Memcheck:Leak
+ fun:malloc
+ fun:gmtsub
+}
+{
+ mac-system-init-leak-1
+ Memcheck:Leak
+ ...
+ fun:_libxpc_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-system-init-leak-2
+ Memcheck:Leak
+ ...
+ fun:__keymgr_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-puts-leak
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ ...
+ fun:puts
+}