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

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--README.md13
-rw-r--r--include/git2/sys/refs.h11
-rw-r--r--include/git2/tree.h6
-rw-r--r--src/checkout.c28
-rw-r--r--src/config_cache.c2
-rw-r--r--src/index.c73
-rw-r--r--src/notes.c4
-rw-r--r--src/path.c256
-rw-r--r--src/path.h43
-rw-r--r--src/refdb_fs.c12
-rw-r--r--src/repository.c25
-rw-r--r--src/repository.h26
-rw-r--r--src/transports/local.c52
-rw-r--r--src/tree.c30
-rw-r--r--src/tree.h1
-rw-r--r--src/util.c91
-rw-r--r--src/util.h12
-rw-r--r--src/win32/findfile.c1
-rw-r--r--src/win32/path_w32.c305
-rw-r--r--src/win32/path_w32.h82
-rw-r--r--src/win32/posix.h1
-rw-r--r--src/win32/posix_w32.c67
-rw-r--r--src/win32/utf-conv.c42
-rw-r--r--src/win32/utf-conv.h39
-rw-r--r--src/win32/w32_util.h1
-rw-r--r--tests/checkout/nasty.c325
-rw-r--r--tests/clar.c36
-rw-r--r--tests/clar.h6
-rw-r--r--tests/config/include.c4
-rw-r--r--tests/core/link.c19
-rw-r--r--tests/index/tests.c130
-rw-r--r--tests/network/fetchlocal.c34
-rw-r--r--tests/object/tree/attributes.c30
-rw-r--r--tests/object/tree/duplicateentries.c4
-rw-r--r--tests/object/tree/write.c72
-rw-r--r--tests/path/core.c254
-rw-r--r--tests/path/win32.c214
-rw-r--r--tests/refs/create.c48
-rw-r--r--tests/repo/iterator.c4
-rw-r--r--tests/resources/nasty/.gitted/HEAD1
-rw-r--r--tests/resources/nasty/.gitted/indexbin0 -> 120 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b3
-rw-r--r--tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178abin0 -> 46 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159dbin0 -> 53 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631bin0 -> 133 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2bin0 -> 51 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21cbin0 -> 45 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e971
-rw-r--r--tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a22
-rw-r--r--tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb1072
-rw-r--r--tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373fbin0 -> 62 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/1b/31d55e0c53efbea6c670ece9057b76b5822eea2
-rw-r--r--tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a322
-rw-r--r--tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982bbin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d71
-rw-r--r--tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5bin0 -> 44 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cfbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85abin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb31
-rw-r--r--tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a2
-rw-r--r--tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c2
-rw-r--r--tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15bin0 -> 135 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cdbin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054bin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f623
-rw-r--r--tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a2906403
-rw-r--r--tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073bin0 -> 46 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6bin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5bin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73bin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64adbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cbabin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629abin0 -> 138 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb22
-rw-r--r--tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819dbin0 -> 51 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1dbin0 -> 62 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185abin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fabin0 -> 23 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51bin0 -> 45 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf2
-rw-r--r--tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52faebin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e2
-rw-r--r--tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf03
-rw-r--r--tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafdbin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e291
-rw-r--r--tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68bin0 -> 44 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b9113
-rw-r--r--tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593dbin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67cabin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17cbin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c47734915692
-rw-r--r--tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4ebin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a21722
-rw-r--r--tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aeebin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd3
-rw-r--r--tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b12
-rw-r--r--tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6bin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f1953
-rw-r--r--tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f42
-rw-r--r--tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247bin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f2
-rw-r--r--tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f42
-rw-r--r--tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc6912
-rw-r--r--tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aecebin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16ebin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d18852
-rw-r--r--tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7bin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfebin0 -> 43 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320cbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8bin0 -> 43 bytes
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_colon1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_dot1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_path_two1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_101
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_111
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_121
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_131
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_141
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_151
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_161
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_21
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_31
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_41
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_51
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_61
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_71
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_81
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_91
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde11
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde21
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde31
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/master1
169 files changed, 2311 insertions, 197 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8b3353f0..b01f48138 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -141,3 +141,7 @@ v0.21 + 1
* git_libgit2_init() and git_libgit2_shutdown() now return the number of
initializations of the library, so consumers may schedule work on the
first initialization.
+
+* git_treebuilder_create now takes a repository so that it can query
+ repository configuration. Subsequently, git_treebuilder_write no
+ longer takes a repository.
diff --git a/README.md b/README.md
index ec74f23ea..2b3ee27d4 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,19 @@ dependencies, it can make use of a few libraries to add to it:
- LibSSH2 to enable the SSH transport
- iconv (OSX) to handle the HFS+ path encoding peculiarities
+Initialization
+===============
+
+The library needs to keep track of some global state. Call
+
+ git_libgit2_init();
+
+before calling any other libgit2 functions. You can call this function many times. A matching number of calls to
+
+ git_libgit2_shutdown();
+
+will free the resources.
+
Threading
=========
diff --git a/include/git2/sys/refs.h b/include/git2/sys/refs.h
index dd95ca12c..d2ce2e0b9 100644
--- a/include/git2/sys/refs.h
+++ b/include/git2/sys/refs.h
@@ -12,6 +12,15 @@
#include "git2/oid.h"
/**
+ * @file git2/sys/refs.h
+ * @brief Low-level Git ref creation
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
* Create a new direct reference from an OID.
*
* @param name the reference name
@@ -35,4 +44,6 @@ GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
const char *name,
const char *target);
+/** @} */
+GIT_END_DECL
#endif
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 42b68193e..0f7616210 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -247,11 +247,12 @@ GIT_EXTERN(int) git_tree_entry_to_object(
* entries and will have to be filled manually.
*
* @param out Pointer where to store the tree builder
+ * @param repo Repository in which to store the object
* @param source Source tree to initialize the builder (optional)
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_treebuilder_create(
- git_treebuilder **out, const git_tree *source);
+ git_treebuilder **out, git_repository *repo, const git_tree *source);
/**
* Clear all the entires in the builder
@@ -368,12 +369,11 @@ GIT_EXTERN(void) git_treebuilder_filter(
* identifying SHA1 hash will be stored in the `id` pointer.
*
* @param id Pointer to store the OID of the newly written tree
- * @param repo Repository in which to store the object
* @param bld Tree builder to write
* @return 0 or an error code
*/
GIT_EXTERN(int) git_treebuilder_write(
- git_oid *id, git_repository *repo, git_treebuilder *bld);
+ git_oid *id, git_treebuilder *bld);
/** Callback for the tree traversal method */
diff --git a/src/checkout.c b/src/checkout.c
index 4e879e36f..a3a46011e 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1172,6 +1172,30 @@ static int checkout_get_remove_conflicts(
return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data);
}
+static int checkout_verify_paths(
+ git_repository *repo,
+ int action,
+ git_diff_delta *delta)
+{
+ unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT;
+
+ if (action & CHECKOUT_ACTION__REMOVE) {
+ if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
+ giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path);
+ return -1;
+ }
+ }
+
+ if (action & ~CHECKOUT_ACTION__REMOVE) {
+ if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
+ giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->old_file.path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int checkout_get_actions(
uint32_t **actions_ptr,
size_t **counts_ptr,
@@ -1205,7 +1229,9 @@ static int checkout_get_actions(
}
git_vector_foreach(deltas, i, delta) {
- error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec);
+ if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0)
+ error = checkout_verify_paths(data->repo, act, delta);
+
if (error != 0)
goto fail;
diff --git a/src/config_cache.c b/src/config_cache.c
index 45c39ce17..d397a4bab 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -76,6 +76,8 @@ static struct map_data _cvar_maps[] = {
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
{"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
{"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
+ {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
+ {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
};
int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
diff --git a/src/index.c b/src/index.c
index d3bc081a5..2db7c8ea5 100644
--- a/src/index.c
+++ b/src/index.c
@@ -762,23 +762,35 @@ void git_index_entry__init_from_stat(
entry->file_size = st->st_size;
}
-static git_index_entry *index_entry_alloc(const char *path)
+static int index_entry_create(
+ git_index_entry **out,
+ git_repository *repo,
+ const char *path)
{
size_t pathlen = strlen(path);
- struct entry_internal *entry =
- git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
- if (!entry)
- return NULL;
+ struct entry_internal *entry;
+
+ if (!git_path_isvalid(repo, path,
+ GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) {
+ giterr_set(GITERR_INDEX, "Invalid path: '%s'", path);
+ return -1;
+ }
+
+ entry = git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
+ GITERR_CHECK_ALLOC(entry);
entry->pathlen = pathlen;
memcpy(entry->path, path, pathlen);
entry->entry.path = entry->path;
- return (git_index_entry *)entry;
+ *out = (git_index_entry *)entry;
+ return 0;
}
static int index_entry_init(
- git_index_entry **entry_out, git_index *index, const char *rel_path)
+ git_index_entry **entry_out,
+ git_index *index,
+ const char *rel_path)
{
int error = 0;
git_index_entry *entry = NULL;
@@ -790,14 +802,17 @@ static int index_entry_init(
"Could not initialize index entry. "
"Index is not backed up by an existing repository.");
+ if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0)
+ return -1;
+
/* write the blob to disk and get the oid and stat info */
error = git_blob__create_from_paths(
&oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
- if (error < 0)
- return error;
- entry = index_entry_alloc(rel_path);
- GITERR_CHECK_ALLOC(entry);
+ if (error < 0) {
+ index_entry_free(entry);
+ return error;
+ }
entry->id = oid;
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
@@ -853,7 +868,10 @@ static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
tgt->path = tgt_path; /* reset to existing path data */
}
-static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
+static int index_entry_dup(
+ git_index_entry **out,
+ git_repository *repo,
+ const git_index_entry *src)
{
git_index_entry *entry;
@@ -862,11 +880,11 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
return 0;
}
- *out = entry = index_entry_alloc(src->path);
- GITERR_CHECK_ALLOC(entry);
+ if (index_entry_create(&entry, repo, src->path) < 0)
+ return -1;
index_entry_cpy(entry, src);
-
+ *out = entry;
return 0;
}
@@ -1131,7 +1149,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
return -1;
}
- if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
+ if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0)
return ret;
@@ -1251,9 +1269,9 @@ int git_index_conflict_add(git_index *index,
assert (index);
- if ((ret = index_entry_dup(&entries[0], ancestor_entry)) < 0 ||
- (ret = index_entry_dup(&entries[1], our_entry)) < 0 ||
- (ret = index_entry_dup(&entries[2], their_entry)) < 0)
+ if ((ret = index_entry_dup(&entries[0], INDEX_OWNER(index), ancestor_entry)) < 0 ||
+ (ret = index_entry_dup(&entries[1], INDEX_OWNER(index), our_entry)) < 0 ||
+ (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0)
goto on_error;
for (i = 0; i < 3; i++) {
@@ -1770,7 +1788,10 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
}
static size_t read_entry(
- git_index_entry **out, const void *buffer, size_t buffer_size)
+ git_index_entry **out,
+ git_index *index,
+ const void *buffer,
+ size_t buffer_size)
{
size_t path_length, entry_size;
const char *path_ptr;
@@ -1834,7 +1855,7 @@ static size_t read_entry(
entry.path = (char *)path_ptr;
- if (index_entry_dup(out, &entry) < 0)
+ if (index_entry_dup(out, INDEX_OWNER(index), &entry) < 0)
return 0;
return entry_size;
@@ -1935,7 +1956,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
git_index_entry *entry;
- size_t entry_size = read_entry(&entry, buffer, buffer_size);
+ size_t entry_size = read_entry(&entry, index, buffer, buffer_size);
/* 0 bytes read means an object corruption */
if (entry_size == 0) {
@@ -2296,6 +2317,7 @@ int git_index_entry_stage(const git_index_entry *entry)
}
typedef struct read_tree_data {
+ git_index *index;
git_vector *old_entries;
git_vector *new_entries;
git_vector_cmp entry_cmp;
@@ -2316,8 +2338,8 @@ static int read_tree_cb(
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
return -1;
- entry = index_entry_alloc(path.ptr);
- GITERR_CHECK_ALLOC(entry);
+ if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0)
+ return -1;
entry->mode = tentry->attr;
entry->id = tentry->oid;
@@ -2357,6 +2379,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
+ data.index = index;
data.old_entries = &index->entries;
data.new_entries = &entries;
data.entry_cmp = index->entries_search;
@@ -2476,7 +2499,7 @@ int git_index_add_all(
break;
/* make the new entry to insert */
- if ((error = index_entry_dup(&entry, wd)) < 0)
+ if ((error = index_entry_dup(&entry, INDEX_OWNER(index), wd)) < 0)
break;
entry->id = blobid;
diff --git a/src/notes.c b/src/notes.c
index 8fdf388ab..a0bc0d355 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -107,7 +107,7 @@ static int tree_write(
const git_tree_entry *entry;
git_oid tree_oid;
- if ((error = git_treebuilder_create(&tb, source_tree)) < 0)
+ if ((error = git_treebuilder_create(&tb, repo, source_tree)) < 0)
goto cleanup;
if (object_oid) {
@@ -119,7 +119,7 @@ static int tree_write(
goto cleanup;
}
- if ((error = git_treebuilder_write(&tree_oid, repo, tb)) < 0)
+ if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
goto cleanup;
error = git_tree_lookup(out, repo, &tree_oid);
diff --git a/src/path.c b/src/path.c
index effe2fff1..0bad96242 100644
--- a/src/path.c
+++ b/src/path.c
@@ -7,6 +7,7 @@
#include "common.h"
#include "path.h"
#include "posix.h"
+#include "repository.h"
#ifdef GIT_WIN32
#include "win32/posix.h"
#include "win32/w32_util.h"
@@ -1238,3 +1239,258 @@ int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
else
return git_buf_sets(local_path_out, url_or_path);
}
+
+/* Reject paths like AUX or COM1, or those versions that end in a dot or
+ * colon. ("AUX." or "AUX:")
+ */
+GIT_INLINE(bool) verify_dospath(
+ const char *component,
+ size_t len,
+ const char dospath[3],
+ bool trailing_num)
+{
+ size_t last = trailing_num ? 4 : 3;
+
+ if (len < last || git__strncasecmp(component, dospath, 3) != 0)
+ return true;
+
+ if (trailing_num && (component[3] < '1' || component[3] > '9'))
+ return true;
+
+ return (len > last &&
+ component[last] != '.' &&
+ component[last] != ':');
+}
+
+static int32_t next_hfs_char(const char **in, size_t *len)
+{
+ while (*len) {
+ int32_t codepoint;
+ int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
+ if (cp_len < 0)
+ return -1;
+
+ (*in) += cp_len;
+ (*len) -= cp_len;
+
+ /* these code points are ignored completely */
+ switch (codepoint) {
+ case 0x200c: /* ZERO WIDTH NON-JOINER */
+ case 0x200d: /* ZERO WIDTH JOINER */
+ case 0x200e: /* LEFT-TO-RIGHT MARK */
+ case 0x200f: /* RIGHT-TO-LEFT MARK */
+ case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
+ case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
+ case 0x202c: /* POP DIRECTIONAL FORMATTING */
+ case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
+ case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
+ case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
+ case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
+ case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
+ case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
+ case 0x206e: /* NATIONAL DIGIT SHAPES */
+ case 0x206f: /* NOMINAL DIGIT SHAPES */
+ case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
+ continue;
+ }
+
+ /* fold into lowercase -- this will only fold characters in
+ * the ASCII range, which is perfectly fine, because the
+ * git folder name can only be composed of ascii characters
+ */
+ return tolower(codepoint);
+ }
+ return 0; /* NULL byte -- end of string */
+}
+
+static bool verify_dotgit_hfs(const char *path, size_t len)
+{
+ if (next_hfs_char(&path, &len) != '.' ||
+ next_hfs_char(&path, &len) != 'g' ||
+ next_hfs_char(&path, &len) != 'i' ||
+ next_hfs_char(&path, &len) != 't' ||
+ next_hfs_char(&path, &len) != 0)
+ return true;
+
+ return false;
+}
+
+GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
+{
+ const char *shortname = NULL;
+ size_t i, start, shortname_len = 0;
+
+ /* See if the repo has a custom shortname (not "GIT~1") */
+ if (repo &&
+ (shortname = git_repository__8dot3_name(repo)) &&
+ shortname != git_repository__8dot3_default)
+ shortname_len = strlen(shortname);
+
+ if (len >= 4 && strncasecmp(path, ".git", 4) == 0)
+ start = 4;
+ else if (len >= git_repository__8dot3_default_len &&
+ strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0)
+ start = git_repository__8dot3_default_len;
+ else if (shortname_len && len >= shortname_len &&
+ strncasecmp(path, shortname, shortname_len) == 0)
+ start = shortname_len;
+ else
+ return true;
+
+ /* Reject paths beginning with ".git\" */
+ if (path[start] == '\\')
+ return false;
+
+ for (i = start; i < len; i++) {
+ if (path[i] != ' ' && path[i] != '.')
+ return true;
+ }
+
+ return false;
+}
+
+GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
+{
+ if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
+ return false;
+
+ if (flags & GIT_PATH_REJECT_NT_CHARS) {
+ if (c < 32)
+ return false;
+
+ switch (c) {
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '|':
+ case '?':
+ case '*':
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * We fundamentally don't like some paths when dealing with user-inputted
+ * strings (in checkout or ref names): we don't want dot or dot-dot
+ * anywhere, we want to avoid writing weird paths on Windows that can't
+ * be handled by tools that use the non-\\?\ APIs, we don't want slashes
+ * or double slashes at the end of paths that can make them ambiguous.
+ *
+ * For checkout, we don't want to recurse into ".git" either.
+ */
+static bool verify_component(
+ git_repository *repo,
+ const char *component,
+ size_t len,
+ unsigned int flags)
+{
+ if (len == 0)
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
+ len == 1 && component[0] == '.')
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
+ len == 2 && component[0] == '.' && component[1] == '.')
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
+ return false;
+
+ if (flags & GIT_PATH_REJECT_DOS_PATHS) {
+ if (!verify_dospath(component, len, "CON", false) ||
+ !verify_dospath(component, len, "PRN", false) ||
+ !verify_dospath(component, len, "AUX", false) ||
+ !verify_dospath(component, len, "NUL", false) ||
+ !verify_dospath(component, len, "COM", true) ||
+ !verify_dospath(component, len, "LPT", true))
+ return false;
+ }
+
+ if (flags & GIT_PATH_REJECT_DOT_GIT_HFS &&
+ !verify_dotgit_hfs(component, len))
+ return false;
+
+ if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS &&
+ !verify_dotgit_ntfs(repo, component, len))
+ return false;
+
+ if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
+ (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
+ (flags & GIT_PATH_REJECT_DOT_GIT) &&
+ len == 4 &&
+ component[0] == '.' &&
+ (component[1] == 'g' || component[1] == 'G') &&
+ (component[2] == 'i' || component[2] == 'I') &&
+ (component[3] == 't' || component[3] == 'T'))
+ return false;
+
+ return true;
+}
+
+GIT_INLINE(unsigned int) dotgit_flags(
+ git_repository *repo,
+ unsigned int flags)
+{
+ int protectHFS = 0, protectNTFS = 0;
+
+#ifdef __APPLE__
+ protectHFS = 1;
+#endif
+
+#ifdef GIT_WIN32
+ protectNTFS = 1;
+#endif
+
+ if (repo && !protectHFS)
+ git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
+ if (protectHFS)
+ flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
+
+ if (repo && !protectNTFS)
+ git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
+ if (protectNTFS)
+ flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
+
+ return flags;
+}
+
+bool git_path_isvalid(
+ git_repository *repo,
+ const char *path,
+ unsigned int flags)
+{
+ const char *start, *c;
+
+ /* Upgrade the ".git" checks based on platform */
+ if ((flags & GIT_PATH_REJECT_DOT_GIT))
+ flags = dotgit_flags(repo, flags);
+
+ for (start = c = path; *c; c++) {
+ if (!verify_char(*c, flags))
+ return false;
+
+ if (*c == '/') {
+ if (!verify_component(repo, start, (c - start), flags))
+ return false;
+
+ start = c+1;
+ }
+ }
+
+ return verify_component(repo, start, (c - start), flags);
+}
diff --git a/src/path.h b/src/path.h
index 23d7c2ddb..b753140b2 100644
--- a/src/path.h
+++ b/src/path.h
@@ -462,4 +462,47 @@ extern bool git_path_does_fs_decompose_unicode(const char *root);
extern bool git_path_is_local_file_url(const char *file_url);
extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
+/* Flags to determine path validity in `git_path_isvalid` */
+#define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
+#define GIT_PATH_REJECT_DOT_GIT (1 << 1)
+#define GIT_PATH_REJECT_SLASH (1 << 2)
+#define GIT_PATH_REJECT_BACKSLASH (1 << 3)
+#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
+#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
+#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
+#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
+#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
+#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
+#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
+
+/* Default path safety for writing files to disk: since we use the
+ * Win32 "File Namespace" APIs ("\\?\") we need to protect from
+ * paths that the normal Win32 APIs would not write.
+ */
+#ifdef GIT_WIN32
+# define GIT_PATH_REJECT_DEFAULTS \
+ GIT_PATH_REJECT_TRAVERSAL | \
+ GIT_PATH_REJECT_BACKSLASH | \
+ GIT_PATH_REJECT_TRAILING_DOT | \
+ GIT_PATH_REJECT_TRAILING_SPACE | \
+ GIT_PATH_REJECT_TRAILING_COLON | \
+ GIT_PATH_REJECT_DOS_PATHS | \
+ GIT_PATH_REJECT_NT_CHARS
+#else
+# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
+#endif
+
+/*
+ * Determine whether a path is a valid git path or not - this must not contain
+ * a '.' or '..' component, or a component that is ".git" (in any case).
+ *
+ * `repo` is optional. If specified, it will be used to determine the short
+ * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
+ * in addition to the default of "git~1".
+ */
+extern bool git_path_isvalid(
+ git_repository *repo,
+ const char *path,
+ unsigned int flags);
+
#endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f39ba4f9c..fc41a95d7 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -707,11 +707,16 @@ static int reference_path_available(
static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
{
- int error;
+ int error;
git_buf ref_path = GIT_BUF_INIT;
assert(file && backend && name);
+ if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) {
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
+ return GIT_EINVALIDSPEC;
+ }
+
/* Remove a possibly existing empty directory hierarchy
* which name would collide with the reference name
*/
@@ -1653,6 +1658,11 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char
repo = backend->repo;
+ if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) {
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
+ return GIT_EINVALIDSPEC;
+ }
+
if (retrieve_reflog_path(&log_path, repo, refname) < 0)
return -1;
diff --git a/src/repository.c b/src/repository.c
index 2bab52919..74a966ef3 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -37,6 +37,9 @@
#define GIT_REPO_VERSION 0
+const char *git_repository__8dot3_default = "GIT~1";
+size_t git_repository__8dot3_default_len = 5;
+
static void set_odb(git_repository *repo, git_odb *odb)
{
if (odb) {
@@ -120,6 +123,7 @@ void git_repository_free(git_repository *repo)
git__free(repo->path_repository);
git__free(repo->workdir);
git__free(repo->namespace);
+ git__free(repo->name_8dot3);
git__memzero(repo, sizeof(*repo));
git__free(repo);
@@ -791,6 +795,27 @@ const char *git_repository_get_namespace(git_repository *repo)
return repo->namespace;
}
+const char *git_repository__8dot3_name(git_repository *repo)
+{
+ if (!repo->has_8dot3) {
+ repo->has_8dot3 = 1;
+
+#ifdef GIT_WIN32
+ if (!repo->is_bare) {
+ repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository);
+
+ /* We anticipate the 8.3 name is "GIT~1", so use a static for
+ * easy testing in the common case */
+ if (strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
+ repo->has_8dot3_default = 1;
+ }
+#endif
+ }
+
+ return repo->has_8dot3_default ?
+ git_repository__8dot3_default : repo->name_8dot3;
+}
+
static int check_repositoryformatversion(git_config *config)
{
int version;
diff --git a/src/repository.h b/src/repository.h
index 40e54c1ca..6da8c289b 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -40,6 +40,8 @@ typedef enum {
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
+ GIT_CVAR_PROTECTHFS, /* core.protectHFS */
+ GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
@@ -96,6 +98,10 @@ typedef enum {
/* core.logallrefupdates */
GIT_LOGALLREFUPDATES_UNSET = 2,
GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
+ /* core.protectHFS */
+ GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
+ /* core.protectNTFS */
+ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value;
/* internal repository init flags */
@@ -120,8 +126,11 @@ struct git_repository {
char *path_repository;
char *workdir;
char *namespace;
+ char *name_8dot3;
- unsigned is_bare:1;
+ unsigned is_bare:1,
+ has_8dot3:1,
+ has_8dot3_default:1;
unsigned int lru_counter;
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
@@ -174,4 +183,19 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
+/*
+ * Gets the DOS-compatible 8.3 "short name". This will return only the
+ * short name for the repository directory (ie, "git~1" for ".git"). This
+ * will always return a pointer to `git_repository__8dot3_default` when
+ * "GIT~1" is the short name. This will return NULL for bare repositories,
+ * and systems that do not have a short name.
+ */
+const char *git_repository__8dot3_name(git_repository *repo);
+
+/* The default DOS-compatible 8.3 "short name" for a git repository,
+ * "GIT~1".
+ */
+extern const char *git_repository__8dot3_default;
+extern size_t git_repository__8dot3_default_len;
+
#endif
diff --git a/src/transports/local.c b/src/transports/local.c
index 3846f06d0..d698e0129 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -35,6 +35,9 @@ typedef struct {
int flags;
git_atomic cancelled;
git_repository *repo;
+ git_transport_message_cb progress_cb;
+ git_transport_message_cb error_cb;
+ void *message_cb_payload;
git_vector refs;
unsigned connected : 1,
have_refs : 1;
@@ -494,6 +497,8 @@ static int foreach_cb(void *buf, size_t len, void *payload)
return data->writepack->append(data->writepack, buf, len, data->stats);
}
+static const char *counting_objects_fmt = "Counting objects %d\r";
+
static int local_download_pack(
git_transport *transport,
git_repository *repo,
@@ -510,6 +515,7 @@ static int local_download_pack(
git_packbuilder *pack = NULL;
git_odb_writepack *writepack = NULL;
git_odb *odb = NULL;
+ git_buf progress_info = GIT_BUF_INIT;
if ((error = git_revwalk_new(&walk, t->repo)) < 0)
goto cleanup;
@@ -540,6 +546,13 @@ static int local_download_pack(
git_object_free(obj);
}
+ if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
+ goto cleanup;
+
+ if (t->progress_cb &&
+ (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
+ goto cleanup;
+
/* Walk the objects, building a packfile */
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
goto cleanup;
@@ -561,9 +574,28 @@ static int local_download_pack(
}
git_commit_free(commit);
+
+ git_buf_clear(&progress_info);
+ if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
+ goto cleanup;
+
+ if (t->progress_cb &&
+ (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
+ goto cleanup;
+
}
}
+ /* One last one with the newline */
+ git_buf_clear(&progress_info);
+ git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
+ if ((error = git_buf_putc(&progress_info, '\n')) < 0)
+ goto cleanup;
+
+ if (t->progress_cb &&
+ (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
+ goto cleanup;
+
if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
goto cleanup;
@@ -582,11 +614,30 @@ static int local_download_pack(
cleanup:
if (writepack) writepack->free(writepack);
+ git_buf_free(&progress_info);
git_packbuilder_free(pack);
git_revwalk_free(walk);
return error;
}
+static int local_set_callbacks(
+ git_transport *transport,
+ git_transport_message_cb progress_cb,
+ git_transport_message_cb error_cb,
+ git_transport_certificate_check_cb certificate_check_cb,
+ void *message_cb_payload)
+{
+ transport_local *t = (transport_local *)transport;
+
+ GIT_UNUSED(certificate_check_cb);
+
+ t->progress_cb = progress_cb;
+ t->error_cb = error_cb;
+ t->message_cb_payload = message_cb_payload;
+
+ return 0;
+}
+
static int local_is_connected(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
@@ -656,6 +707,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
GITERR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
+ t->parent.set_callbacks = local_set_callbacks;
t->parent.connect = local_connect;
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
diff --git a/src/tree.c b/src/tree.c
index 6b06dfd77..57cc95387 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -50,14 +50,11 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
return GIT_FILEMODE_BLOB;
}
-static int valid_entry_name(const char *filename)
+static int valid_entry_name(git_repository *repo, const char *filename)
{
return *filename != '\0' &&
- strchr(filename, '/') == NULL &&
- (*filename != '.' ||
- (strcmp(filename, ".") != 0 &&
- strcmp(filename, "..") != 0 &&
- strcmp(filename, DOT_GIT) != 0));
+ git_path_isvalid(repo, filename,
+ GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH);
}
static int entry_sort_cmp(const void *a, const void *b)
@@ -455,7 +452,7 @@ static int append_entry(
git_tree_entry *entry;
int error = 0;
- if (!valid_entry_name(filename))
+ if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
entry = alloc_entry(filename);
@@ -493,7 +490,7 @@ static int write_tree(
return (int)find_next_dir(dirname, index, start);
}
- if ((error = git_treebuilder_create(&bld, NULL)) < 0 || bld == NULL)
+ if ((error = git_treebuilder_create(&bld, repo, NULL)) < 0 || bld == NULL)
return -1;
/*
@@ -564,7 +561,7 @@ static int write_tree(
}
}
- if (git_treebuilder_write(oid, repo, bld) < 0)
+ if (git_treebuilder_write(oid, bld) < 0)
goto on_error;
git_treebuilder_free(bld);
@@ -627,16 +624,21 @@ int git_tree__write_index(
return ret;
}
-int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
+int git_treebuilder_create(
+ git_treebuilder **builder_p,
+ git_repository *repo,
+ const git_tree *source)
{
git_treebuilder *bld;
size_t i;
- assert(builder_p);
+ assert(builder_p && repo);
bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld);
+ bld->repo = repo;
+
if (git_strmap_alloc(&bld->map) < 0) {
git__free(bld);
return -1;
@@ -678,7 +680,7 @@ int git_treebuilder_insert(
if (!valid_filemode(filemode))
return tree_error("Failed to insert entry. Invalid filemode for file", filename);
- if (!valid_entry_name(filename))
+ if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
pos = git_strmap_lookup_index(bld->map, filename);
@@ -738,7 +740,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
return 0;
}
-int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
+int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{
int error = 0;
size_t i, entrycount;
@@ -777,7 +779,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_vector_free(&entries);
if (!error &&
- !(error = git_repository_odb__weakptr(&odb, repo)))
+ !(error = git_repository_odb__weakptr(&odb, bld->repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
git_buf_free(&tree);
diff --git a/src/tree.h b/src/tree.h
index 5d27eb7c9..d01b6fd41 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -26,6 +26,7 @@ struct git_tree {
};
struct git_treebuilder {
+ git_repository *repo;
git_strmap *map;
};
diff --git a/src/util.c b/src/util.c
index 5c305950f..7ee3e2ff9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -250,6 +250,21 @@ int git__prefixcmp_icase(const char *str, const char *prefix)
return strncasecmp(str, prefix, strlen(prefix));
}
+int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+{
+ int s, p;
+
+ while(str_n--) {
+ s = (unsigned char)tolower(*str++);
+ p = (unsigned char)tolower(*prefix++);
+
+ if (s != p)
+ return s - p;
+ }
+
+ return (0 - *prefix);
+}
+
int git__suffixcmp(const char *str, const char *suffix)
{
size_t a = strlen(str);
@@ -649,3 +664,79 @@ void git__insertsort_r(
if (freeswap)
git__free(swapel);
}
+
+static const int8_t utf8proc_utf8class[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int git__utf8_charlen(const uint8_t *str, int str_len)
+{
+ int length, i;
+
+ length = utf8proc_utf8class[str[0]];
+ if (!length)
+ return -1;
+
+ if (str_len >= 0 && length > str_len)
+ return -str_len;
+
+ for (i = 1; i < length; i++) {
+ if ((str[i] & 0xC0) != 0x80)
+ return -i;
+ }
+
+ return length;
+}
+
+int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
+{
+ int length;
+ int32_t uc = -1;
+
+ *dst = -1;
+ length = git__utf8_charlen(str, str_len);
+ if (length < 0)
+ return -1;
+
+ switch (length) {
+ case 1:
+ uc = str[0];
+ break;
+ case 2:
+ uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
+ if (uc < 0x80) uc = -1;
+ break;
+ case 3:
+ uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
+ + (str[2] & 0x3F);
+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
+ (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
+ break;
+ case 4:
+ uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
+ + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
+ if (uc < 0x10000 || uc >= 0x110000) uc = -1;
+ break;
+ }
+
+ if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
+ return -1;
+
+ *dst = uc;
+ return length;
+}
diff --git a/src/util.h b/src/util.h
index 6e57ad8c3..7cfc0d644 100644
--- a/src/util.h
+++ b/src/util.h
@@ -106,6 +106,7 @@ GIT_INLINE(void) git__free(void *ptr)
extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix);
+extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
GIT_INLINE(int) git__signum(int val)
@@ -367,6 +368,17 @@ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
extern size_t git__unescape(char *str);
/*
+ * Iterate through an UTF-8 string, yielding one
+ * codepoint at a time.
+ *
+ * @param str current position in the string
+ * @param str_len size left in the string; -1 if the string is NULL-terminated
+ * @param dst pointer where to store the current codepoint
+ * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
+ */
+extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
+
+/*
* Safely zero-out memory, making sure that the compiler
* doesn't optimize away the operation.
*/
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index 86d4ef5bd..de27dd060 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -5,6 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "path_w32.h"
#include "utf-conv.h"
#include "path.h"
#include "findfile.h"
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
new file mode 100644
index 000000000..d66969c4d
--- /dev/null
+++ b/src/win32/path_w32.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "path.h"
+#include "path_w32.h"
+#include "utf-conv.h"
+
+#define PATH__NT_NAMESPACE L"\\\\?\\"
+#define PATH__NT_NAMESPACE_LEN 4
+
+#define PATH__ABSOLUTE_LEN 3
+
+#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
+
+#define path__is_absolute(p) \
+ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
+
+#define path__is_nt_namespace(p) \
+ (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
+ ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
+
+#define path__is_unc(p) \
+ (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
+
+GIT_INLINE(int) path__cwd(wchar_t *path, int size)
+{
+ int len;
+
+ if ((len = GetCurrentDirectoryW(size, path)) == 0) {
+ errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT;
+ return -1;
+ } else if (len > size) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* The Win32 APIs may return "\\?\" once you've used it first.
+ * But it may not. What a gloriously predictible API!
+ */
+ if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN))
+ return len;
+
+ len -= PATH__NT_NAMESPACE_LEN;
+
+ memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len);
+ return len;
+}
+
+static wchar_t *path__skip_server(wchar_t *path)
+{
+ wchar_t *c;
+
+ for (c = path; *c; c++) {
+ if (path__is_dirsep(*c))
+ return c + 1;
+ }
+
+ return c;
+}
+
+static wchar_t *path__skip_prefix(wchar_t *path)
+{
+ if (path__is_nt_namespace(path)) {
+ path += PATH__NT_NAMESPACE_LEN;
+
+ if (wcsncmp(path, L"UNC\\", 4) == 0)
+ path = path__skip_server(path + 4);
+ else if (path__is_absolute(path))
+ path += PATH__ABSOLUTE_LEN;
+ } else if (path__is_absolute(path)) {
+ path += PATH__ABSOLUTE_LEN;
+ } else if (path__is_unc(path)) {
+ path = path__skip_server(path + 2);
+ }
+
+ return path;
+}
+
+int git_win32_path_canonicalize(git_win32_path path)
+{
+ wchar_t *base, *from, *to, *next;
+ size_t len;
+
+ base = to = path__skip_prefix(path);
+
+ /* Unposixify if the prefix */
+ for (from = path; from < to; from++) {
+ if (*from == L'/')
+ *from = L'\\';
+ }
+
+ while (*from) {
+ for (next = from; *next; ++next) {
+ if (*next == L'/') {
+ *next = L'\\';
+ break;
+ }
+
+ if (*next == L'\\')
+ break;
+ }
+
+ len = next - from;
+
+ if (len == 1 && from[0] == L'.')
+ /* do nothing with singleton dot */;
+
+ else if (len == 2 && from[0] == L'.' && from[1] == L'.') {
+ if (to == base) {
+ /* no more path segments to strip, eat the "../" */
+ if (*next == L'\\')
+ len++;
+
+ base = to;
+ } else {
+ /* back up a path segment */
+ while (to > base && to[-1] == L'\\') to--;
+ while (to > base && to[-1] != L'\\') to--;
+ }
+ } else {
+ if (*next == L'\\' && *from != L'\\')
+ len++;
+
+ if (to != from)
+ memmove(to, from, sizeof(wchar_t) * len);
+
+ to += len;
+ }
+
+ from += len;
+
+ while (*from == L'\\') from++;
+ }
+
+ /* Strip trailing backslashes */
+ while (to > base && to[-1] == L'\\') to--;
+
+ *to = L'\0';
+
+ return (to - path);
+}
+
+int git_win32_path__cwd(wchar_t *out, size_t len)
+{
+ int cwd_len;
+
+ if ((cwd_len = path__cwd(out, len)) < 0)
+ return -1;
+
+ /* UNC paths */
+ if (wcsncmp(L"\\\\", out, 2) == 0) {
+ /* Our buffer must be at least 5 characters larger than the
+ * current working directory: we swallow one of the leading
+ * '\'s, but we we add a 'UNC' specifier to the path, plus
+ * a trailing directory separator, plus a NUL.
+ */
+ if (cwd_len > MAX_PATH - 4) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ memmove(out+2, out, sizeof(wchar_t) * cwd_len);
+ out[0] = L'U';
+ out[1] = L'N';
+ out[2] = L'C';
+
+ cwd_len += 2;
+ }
+
+ /* Our buffer must be at least 2 characters larger than the current
+ * working directory. (One character for the directory separator,
+ * one for the null.
+ */
+ else if (cwd_len > MAX_PATH - 2) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ return cwd_len;
+}
+
+int git_win32_path_from_utf8(git_win32_path out, const char *src)
+{
+ wchar_t *dest = out;
+
+ /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
+ memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
+ dest += PATH__NT_NAMESPACE_LEN;
+
+ /* See if this is an absolute path (beginning with a drive letter) */
+ if (path__is_absolute(src)) {
+ if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
+ return -1;
+ }
+ /* File-prefixed NT-style paths beginning with \\?\ */
+ else if (path__is_nt_namespace(src)) {
+ /* Skip the NT prefix, the destination already contains it */
+ if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
+ return -1;
+ }
+ /* UNC paths */
+ else if (path__is_unc(src)) {
+ memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4);
+ dest += 4;
+
+ /* Skip the leading "\\" */
+ if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
+ return -1;
+ }
+ /* Absolute paths omitting the drive letter */
+ else if (src[0] == '\\' || src[0] == '/') {
+ if (path__cwd(dest, MAX_PATH) < 0)
+ return -1;
+
+ if (!path__is_absolute(dest)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Skip the drive letter specification ("C:") */
+ if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
+ return -1;
+ }
+ /* Relative paths */
+ else {
+ int cwd_len;
+
+ if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
+ return -1;
+
+ dest[cwd_len++] = L'\\';
+
+ if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
+ return -1;
+ }
+
+ return git_win32_path_canonicalize(out);
+}
+
+int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
+{
+ char *out = dest;
+ int len;
+
+ /* Strip NT namespacing "\\?\" */
+ if (path__is_nt_namespace(src)) {
+ src += 4;
+
+ /* "\\?\UNC\server\share" -> "\\server\share" */
+ if (wcsncmp(src, L"UNC\\", 4) == 0) {
+ src += 4;
+
+ memcpy(dest, "\\\\", 2);
+ out = dest + 2;
+ }
+ }
+
+ if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
+ return len;
+
+ git_path_mkposix(dest);
+
+ return len;
+}
+
+char *git_win32_path_8dot3_name(const char *path)
+{
+ git_win32_path longpath, shortpath;
+ wchar_t *start;
+ char *shortname;
+ int len, namelen = 1;
+
+ if (git_win32_path_from_utf8(longpath, path) < 0)
+ return NULL;
+
+ len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
+
+ while (len && shortpath[len-1] == L'\\')
+ shortpath[--len] = L'\0';
+
+ if (len == 0 || len >= GIT_WIN_PATH_UTF16)
+ return NULL;
+
+ for (start = shortpath + (len - 1);
+ start > shortpath && *(start-1) != '/' && *(start-1) != '\\';
+ start--)
+ namelen++;
+
+ /* We may not have actually been given a short name. But if we have,
+ * it will be in the ASCII byte range, so we don't need to worry about
+ * multi-byte sequences and can allocate naively.
+ */
+ if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
+ return NULL;
+
+ if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
+ return NULL;
+
+ return shortname;
+}
diff --git a/src/win32/path_w32.h b/src/win32/path_w32.h
new file mode 100644
index 000000000..033afbb0f
--- /dev/null
+++ b/src/win32/path_w32.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_path_w32_h__
+#define INCLUDE_git_path_w32_h__
+
+#include "common.h"
+
+/*
+ * Provides a large enough buffer to support Windows paths: MAX_PATH is
+ * 260, corresponding to a maximum path length of 259 characters plus a
+ * NULL terminator. Prefixing with "\\?\" adds 4 characters, but if the
+ * original was a UNC path, then we turn "\\server\share" into
+ * "\\?\UNC\server\share". So we replace the first two characters with
+ * 8 characters, a net gain of 6, so the maximum length is MAX_PATH+6.
+ */
+#define GIT_WIN_PATH_UTF16 MAX_PATH+6
+
+/* Maximum size of a UTF-8 Win32 path. We remove the "\\?\" or "\\?\UNC\"
+ * prefixes for presentation, bringing us back to 259 (non-NULL)
+ * characters. UTF-8 does have 4-byte sequences, but they are encoded in
+ * UTF-16 using surrogate pairs, which takes up the space of two characters.
+ * Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8
+ * (6 bytes) than one surrogate pair (4 bytes).
+ */
+#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
+
+/*
+ * The length of a Windows "shortname", for 8.3 compatibility.
+ */
+#define GIT_WIN_PATH_SHORTNAME 13
+
+/* Win32 path types */
+typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
+typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
+
+/**
+ * Create a Win32 path (in UCS-2 format) from a UTF-8 string.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
+ */
+extern int git_win32_path_from_utf8(git_win32_path dest, const char *src);
+
+/**
+ * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the
+ * Win32 APIs: remove multiple directory separators, squashing to a single one,
+ * strip trailing directory separators, ensure directory separators are all
+ * canonical (always backslashes, never forward slashes) and process any
+ * directory entries of '.' or '..'.
+ *
+ * This processes the buffer in place.
+ *
+ * @param path The buffer to process
+ * @return The new length of the buffer, in wchar_t's (not counting the NULL terminator)
+ */
+extern int git_win32_path_canonicalize(git_win32_path path);
+
+/**
+ * Create an internal format (posix-style) UTF-8 path from a Win32 UCS-2 path.
+ *
+ * @param dest The buffer to receive the UTF-8 string.
+ * @param src The wide string to convert.
+ * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
+ */
+extern int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src);
+
+/**
+ * Get the short name for the terminal path component in the given path.
+ * For example, given "C:\Foo\Bar\Asdf.txt", this will return the short name
+ * for the file "Asdf.txt".
+ *
+ * @param path The given path in UTF-8
+ * @return The name of the shortname for the given path
+ */
+extern char *git_win32_path_8dot3_name(const char *path);
+
+#endif
diff --git a/src/win32/posix.h b/src/win32/posix.h
index e055a77d0..104966edc 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -9,6 +9,7 @@
#include "common.h"
#include "../posix.h"
+#include "path_w32.h"
#include "utf-conv.h"
#include "dir.h"
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 7b4555719..e446ccab0 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -7,6 +7,7 @@
#include "../posix.h"
#include "../fileops.h"
#include "path.h"
+#include "path_w32.h"
#include "utf-conv.h"
#include "repository.h"
#include "reparse.h"
@@ -35,22 +36,6 @@
/* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
-/* Helper function which converts UTF-8 paths to UTF-16.
- * On failure, errno is set. */
-static int utf8_to_16_with_errno(git_win32_path dest, const char *src)
-{
- int len = git_win32_path_from_utf8(dest, src);
-
- if (len < 0) {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- errno = ENAMETOOLONG;
- else
- errno = EINVAL; /* Bad code point, presumably */
- }
-
- return len;
-}
-
int p_ftruncate(int fd, long size)
{
#if defined(_MSC_VER) && _MSC_VER >= 1500
@@ -66,7 +51,7 @@ int p_mkdir(const char *path, mode_t mode)
GIT_UNUSED(mode);
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
return _wmkdir(buf);
@@ -85,7 +70,7 @@ int p_unlink(const char *path)
git_win32_path buf;
int error;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
error = _wunlink(buf);
@@ -292,7 +277,7 @@ static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
git_win32_path path_w;
int len;
- if ((len = utf8_to_16_with_errno(path_w, path)) < 0)
+ if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
return -1;
git_win32__path_trim_end(path_w, len);
@@ -323,7 +308,7 @@ int p_readlink(const char *path, char *buf, size_t bufsiz)
* could occur in the middle of the encoding of a code point,
* we need to buffer the result on the stack. */
- if (utf8_to_16_with_errno(path_w, path) < 0 ||
+ if (git_win32_path_from_utf8(path_w, path) < 0 ||
readlink_w(target_w, path_w) < 0 ||
(len = git_win32_path_to_utf8(target, target_w)) < 0)
return -1;
@@ -347,7 +332,7 @@ int p_open(const char *path, int flags, ...)
git_win32_path buf;
mode_t mode = 0;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
if (flags & O_CREAT) {
@@ -365,7 +350,7 @@ int p_creat(const char *path, mode_t mode)
{
git_win32_path buf;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode);
@@ -463,7 +448,7 @@ int p_stat(const char* path, struct stat* buf)
git_win32_path path_w;
int len;
- if ((len = utf8_to_16_with_errno(path_w, path)) < 0)
+ if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
return -1;
git_win32__path_trim_end(path_w, len);
@@ -483,7 +468,7 @@ int p_chdir(const char* path)
{
git_win32_path buf;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
return _wchdir(buf);
@@ -493,7 +478,7 @@ int p_chmod(const char* path, mode_t mode)
{
git_win32_path buf;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
return _wchmod(buf, mode);
@@ -504,7 +489,7 @@ int p_rmdir(const char* path)
git_win32_path buf;
int error;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
error = _wrmdir(buf);
@@ -533,7 +518,7 @@ char *p_realpath(const char *orig_path, char *buffer)
{
git_win32_path orig_path_w, buffer_w;
- if (utf8_to_16_with_errno(orig_path_w, orig_path) < 0)
+ if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
return NULL;
/* Note that if the path provided is a relative path, then the current directory
@@ -554,20 +539,17 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL;
}
- /* Convert the path to UTF-8. */
- if (buffer) {
- /* If the caller provided a buffer, then it is assumed to be GIT_WIN_PATH_UTF8
- * characters in size. If it isn't, then we may overflow. */
- if (git__utf16_to_8(buffer, GIT_WIN_PATH_UTF8, buffer_w) < 0)
- return NULL;
- } else {
- /* If the caller did not provide a buffer, then we allocate one for the caller
- * from the heap. */
- if (git__utf16_to_8_alloc(&buffer, buffer_w) < 0)
- return NULL;
+ if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
+ errno = ENOMEM;
+ return NULL;
}
- /* Convert backslashes to forward slashes */
+ /* Convert the path to UTF-8. If the caller provided a buffer, then it
+ * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
+ * then we may overflow. */
+ if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
+ return NULL;
+
git_path_mkposix(buffer);
return buffer;
@@ -608,6 +590,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...)
return r;
}
+/* TODO: wut? */
int p_mkstemp(char *tmp_path)
{
#if defined(_MSC_VER) && _MSC_VER >= 1500
@@ -625,7 +608,7 @@ int p_access(const char* path, mode_t mode)
{
git_win32_path buf;
- if (utf8_to_16_with_errno(buf, path) < 0)
+ if (git_win32_path_from_utf8(buf, path) < 0)
return -1;
return _waccess(buf, mode);
@@ -664,8 +647,8 @@ int p_rename(const char *from, const char *to)
int rename_succeeded;
int error;
- if (utf8_to_16_with_errno(wfrom, from) < 0 ||
- utf8_to_16_with_errno(wto, to) < 0)
+ if (git_win32_path_from_utf8(wfrom, from) < 0 ||
+ git_win32_path_from_utf8(wto, to) < 0)
return -1;
/* wait up to 50ms if file is locked by another thread or process */
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index b9ccfb5e5..b0205b019 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -26,6 +26,14 @@ GIT_INLINE(DWORD) get_wc_flags(void)
return flags;
}
+GIT_INLINE(void) git__set_errno(void)
+{
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ errno = ENAMETOOLONG;
+ else
+ errno = EINVAL;
+}
+
/**
* Converts a UTF-8 string to wide characters.
*
@@ -36,10 +44,15 @@ GIT_INLINE(DWORD) get_wc_flags(void)
*/
int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
{
+ int len;
+
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */
- return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1;
+ if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0)
+ git__set_errno();
+
+ return len;
}
/**
@@ -52,10 +65,15 @@ int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
*/
int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
{
+ int len;
+
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */
- return WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1;
+ if ((len = WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0)
+ git__set_errno();
+
+ return len;
}
/**
@@ -76,17 +94,23 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
/* Length of -1 indicates NULL termination of the input string */
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
- if (!utf16_size)
+ if (!utf16_size) {
+ git__set_errno();
return -1;
+ }
*dest = git__malloc(utf16_size * sizeof(wchar_t));
- if (!*dest)
+ if (!*dest) {
+ errno = ENOMEM;
return -1;
+ }
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size);
if (!utf16_size) {
+ git__set_errno();
+
git__free(*dest);
*dest = NULL;
}
@@ -116,17 +140,23 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
/* Length of -1 indicates NULL termination of the input string */
utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL);
- if (!utf8_size)
+ if (!utf8_size) {
+ git__set_errno();
return -1;
+ }
*dest = git__malloc(utf8_size);
- if (!*dest)
+ if (!*dest) {
+ errno = ENOMEM;
return -1;
+ }
utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL);
if (!utf8_size) {
+ git__set_errno();
+
git__free(*dest);
*dest = NULL;
}
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index a480cd93e..89cdb96da 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -10,21 +10,6 @@
#include <wchar.h>
#include "common.h"
-/* Equal to the Win32 MAX_PATH constant. The maximum path length is 259
- * characters plus a NULL terminator. */
-#define GIT_WIN_PATH_UTF16 260
-
-/* Maximum size of a UTF-8 Win32 path. UTF-8 does have 4-byte sequences,
- * but they are encoded in UTF-16 using surrogate pairs, which takes up
- * the space of two characters. Two characters in the range U+0800 ->
- * U+FFFF take up more space in UTF-8 (6 bytes) than one surrogate pair
- * (4 bytes). */
-#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
-
-/* Win32 path types */
-typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
-typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
-
/**
* Converts a UTF-8 string to wide characters.
*
@@ -67,28 +52,4 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src);
*/
int git__utf16_to_8_alloc(char **dest, const wchar_t *src);
-/**
- * Converts a UTF-8 Win32 path to wide characters.
- *
- * @param dest The buffer to receive the wide string.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-GIT_INLINE(int) git_win32_path_from_utf8(git_win32_path dest, const char *src)
-{
- return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src);
-}
-
-/**
- * Converts a wide Win32 path to UTF-8.
- *
- * @param dest The buffer to receive the UTF-8 string.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-GIT_INLINE(int) git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
-{
- return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src);
-}
-
#endif
diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h
index a1d388af5..9c1b94359 100644
--- a/src/win32/w32_util.h
+++ b/src/win32/w32_util.h
@@ -9,6 +9,7 @@
#define INCLUDE_w32_util_h__
#include "utf-conv.h"
+#include "path_w32.h"
GIT_INLINE(bool) git_win32__isalpha(wchar_t c)
{
diff --git a/tests/checkout/nasty.c b/tests/checkout/nasty.c
new file mode 100644
index 000000000..c07d9382a
--- /dev/null
+++ b/tests/checkout/nasty.c
@@ -0,0 +1,325 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "buffer.h"
+#include "fileops.h"
+
+static const char *repo_name = "nasty";
+static git_repository *repo;
+static git_checkout_options checkout_opts;
+
+void test_checkout_nasty__initialize(void)
+{
+ repo = cl_git_sandbox_init(repo_name);
+
+ GIT_INIT_STRUCTURE(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+}
+
+void test_checkout_nasty__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_checkout_fails(const char *refname, const char *filename)
+{
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, repo_name, filename));
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_fail(git_checkout_tree(repo, (const git_object *)commit, &opts));
+ cl_assert(!git_path_exists(path.ptr));
+
+ git_commit_free(commit);
+ git_buf_free(&path);
+}
+
+/* A tree that contains ".git" as a tree, with a blob inside
+ * (".git/foobar").
+ */
+void test_checkout_nasty__dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains ".GIT" as a tree, with a blob inside
+ * (".GIT/foobar").
+ */
+void test_checkout_nasty__dotcapitalgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotcapitalgit_tree", ".GIT/foobar");
+}
+
+/* A tree that contains a tree ".", with a blob inside ("./foobar").
+ */
+void test_checkout_nasty__dot_tree(void)
+{
+ test_checkout_fails("refs/heads/dot_tree", "foobar");
+}
+
+/* A tree that contains a tree ".", with a tree ".git", with a blob
+ * inside ("./.git/foobar").
+ */
+void test_checkout_nasty__dot_dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dot_dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains a tree, with a tree "..", with a tree ".git", with a
+ * blob inside ("foo/../.git/foobar").
+ */
+void test_checkout_nasty__dotdot_dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains a tree, with a tree "..", with a blob inside
+ * ("foo/../foobar").
+ */
+void test_checkout_nasty__dotdot_tree(void)
+{
+ test_checkout_fails("refs/heads/dotdot_tree", "foobar");
+}
+
+/* A tree that contains a blob with the rogue name ".git/foobar" */
+void test_checkout_nasty__dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name ".GIT/foobar" */
+void test_checkout_nasty__dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "./.git/foobar" */
+void test_checkout_nasty__dot_dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dot_dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "./.GIT/foobar" */
+void test_checkout_nasty__dot_dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dot_dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../.git/foobar" */
+void test_checkout_nasty__dotdot_dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../.GIT/foobar" */
+void test_checkout_nasty__dotdot_dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/." */
+void test_checkout_nasty__dot_path(void)
+{
+ test_checkout_fails("refs/heads/dot_path", "./foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/." */
+void test_checkout_nasty__dot_path_two(void)
+{
+ test_checkout_fails("refs/heads/dot_path_two", "foo/.");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../foobar" */
+void test_checkout_nasty__dotdot_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_path", "foobar");
+}
+
+/* A tree that contains an entry with a backslash ".git\foobar" */
+void test_checkout_nasty__dotgit_backslash_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry with a backslash ".GIT\foobar" */
+void test_checkout_nasty__dotcapitalgit_backslash_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
+#endif
+}
+
+/* A tree that contains an entry with a backslash ".\.GIT\foobar" */
+void test_checkout_nasty__dot_backslash_dotcapitalgit_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_backslash_dotcapitalgit_path", ".GIT/foobar");
+#endif
+}
+
+/* A tree that contains an entry ".git.", because Win32 APIs will drop the
+ * trailing slash.
+ */
+void test_checkout_nasty__dot_git_dot(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git~1", because that is typically the
+ * short name for ".git".
+ */
+void test_checkout_nasty__git_tilde1(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git~2", when we have forced the short
+ * name for ".git" into "GIT~2".
+ */
+void test_checkout_nasty__git_custom_shortname(void)
+{
+#ifdef GIT_WIN32
+ cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
+ cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
+ test_checkout_fails("refs/heads/git_tilde2", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git~3", which should be allowed, since
+ * it is not the typical short name ("GIT~1") or the actual short name
+ * ("GIT~2") for ".git".
+ */
+void test_checkout_nasty__only_looks_like_a_git_shortname(void)
+{
+#ifdef GIT_WIN32
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
+ cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, repo, "refs/heads/git_tilde3"));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
+ cl_assert(git_path_exists("nasty/git~3/foobar"));
+
+ git_commit_free(commit);
+#endif
+}
+
+/* A tree that contains an entry "git:", because Win32 APIs will reject
+ * that as looking too similar to a drive letter.
+ */
+void test_checkout_nasty__dot_git_colon(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_colon", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git:foo", because Win32 APIs will turn
+ * that into ".git".
+ */
+void test_checkout_nasty__dot_git_colon_stuff(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_colon_stuff", ".git/foobar");
+#endif
+}
+
+/* Trees that contains entries with a tree ".git" that contain
+ * byte sequences:
+ * { 0xe2, 0x80, 0x8c }
+ * { 0xe2, 0x80, 0x8d }
+ * { 0xe2, 0x80, 0x8e }
+ * { 0xe2, 0x80, 0x8f }
+ * { 0xe2, 0x80, 0xaa }
+ * { 0xe2, 0x80, 0xab }
+ * { 0xe2, 0x80, 0xac }
+ * { 0xe2, 0x80, 0xad }
+ * { 0xe2, 0x81, 0xae }
+ * { 0xe2, 0x81, 0xaa }
+ * { 0xe2, 0x81, 0xab }
+ * { 0xe2, 0x81, 0xac }
+ * { 0xe2, 0x81, 0xad }
+ * { 0xe2, 0x81, 0xae }
+ * { 0xe2, 0x81, 0xaf }
+ * { 0xef, 0xbb, 0xbf }
+ * Because these map to characters that HFS filesystems "ignore". Thus
+ * ".git<U+200C>" will map to ".git".
+ */
+void test_checkout_nasty__dot_git_hfs_ignorable(void)
+{
+#ifdef __APPLE__
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
+#endif
+}
+
+void test_checkout_nasty__honors_core_protecthfs(void)
+{
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
+}
+
+void test_checkout_nasty__honors_core_protectntfs(void)
+{
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
+ test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
+ test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
+ test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
+}
diff --git a/tests/clar.c b/tests/clar.c
index 154644783..51f163526 100644
--- a/tests/clar.c
+++ b/tests/clar.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <math.h>
#include <stdarg.h>
+#include <wchar.h>
/* required for sandboxing */
#include <sys/types.h>
@@ -525,6 +526,41 @@ void clar__assert_equal(
}
}
}
+ else if (!strcmp("%ls", fmt)) {
+ const wchar_t *wcs1 = va_arg(args, const wchar_t *);
+ const wchar_t *wcs2 = va_arg(args, const wchar_t *);
+ is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2);
+
+ if (!is_equal) {
+ if (wcs1 && wcs2) {
+ int pos;
+ for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
+ wcs1, wcs2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
+ }
+ }
+ }
+ else if(!strcmp("%.*ls", fmt)) {
+ const wchar_t *wcs1 = va_arg(args, const wchar_t *);
+ const wchar_t *wcs2 = va_arg(args, const wchar_t *);
+ int len = va_arg(args, int);
+ is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len);
+
+ if (!is_equal) {
+ if (wcs1 && wcs2) {
+ int pos;
+ for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
+ len, wcs1, len, wcs2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
+ }
+ }
+ }
else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
is_equal = (sz1 == sz2);
diff --git a/tests/clar.h b/tests/clar.h
index f9df72e8c..514203f89 100644
--- a/tests/clar.h
+++ b/tests/clar.h
@@ -74,9 +74,15 @@ void cl_fixture_cleanup(const char *fixture_name);
#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
+#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
+#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
+
#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
+#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+
#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
diff --git a/tests/config/include.c b/tests/config/include.c
index d4af59509..0a931342a 100644
--- a/tests/config/include.c
+++ b/tests/config/include.c
@@ -83,8 +83,8 @@ void test_config_include__depth(void)
cl_git_fail(git_config_open_ondisk(&cfg, "a"));
- unlink("a");
- unlink("b");
+ p_unlink("a");
+ p_unlink("b");
}
void test_config_include__missing(void)
diff --git a/tests/core/link.c b/tests/core/link.c
index 83999ebdf..ec85ec4e0 100644
--- a/tests/core/link.c
+++ b/tests/core/link.c
@@ -197,19 +197,6 @@ static void do_custom_reparse(const char *path)
#endif
-git_buf *unslashify(git_buf *buf)
-{
-#ifdef GIT_WIN32
- size_t i;
-
- for (i = 0; i < buf->size; i++)
- if (buf->ptr[i] == '/')
- buf->ptr[i] = '\\';
-#endif
-
- return buf;
-}
-
void test_core_link__stat_regular_file(void)
{
struct stat st;
@@ -584,7 +571,7 @@ void test_core_link__readlink_symlink(void)
buf[len] = 0;
- cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf);
+ cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_free(&target_path);
}
@@ -607,7 +594,7 @@ void test_core_link__readlink_dangling(void)
buf[len] = 0;
- cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf);
+ cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_free(&target_path);
}
@@ -636,7 +623,7 @@ void test_core_link__readlink_multiple(void)
buf[len] = 0;
- cl_assert_equal_s(git_buf_cstr(unslashify(&path2)), buf);
+ cl_assert_equal_s(git_buf_cstr(&path2), buf);
git_buf_free(&path1);
git_buf_free(&path2);
diff --git a/tests/index/tests.c b/tests/index/tests.c
index 7d544e8f3..a63183e10 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -309,31 +309,145 @@ void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
git_repository_free(bare_repo);
}
+static void add_invalid_filename(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_buf_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+ cl_git_fail(git_index_add_bypath(index, fn));
+ cl_must_pass(p_unlink(path.ptr));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_buf_free(&path);
+ git_index_free(index);
+}
+
/* Test that writing an invalid filename fails */
-void test_index_tests__write_invalid_filename(void)
+void test_index_tests__add_invalid_filename(void)
{
git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+ cl_must_pass(p_mkdir("./invalid/subdir", 0777));
+
+ /* cl_git_mkfile() needs the dir to exist */
+ if (!git_path_exists("./invalid/.GIT"))
+ cl_must_pass(p_mkdir("./invalid/.GIT", 0777));
+ if (!git_path_exists("./invalid/.GiT"))
+ cl_must_pass(p_mkdir("./invalid/.GiT", 0777));
+
+ add_invalid_filename(repo, ".git/hello");
+ add_invalid_filename(repo, ".GIT/hello");
+ add_invalid_filename(repo, ".GiT/hello");
+ add_invalid_filename(repo, "./.git/hello");
+ add_invalid_filename(repo, "./foo");
+ add_invalid_filename(repo, "./bar");
+ add_invalid_filename(repo, "subdir/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+static void replace_char(char *str, char in, char out)
+{
+ char *c = str;
+
+ while (*c++)
+ if (*c == in)
+ *c = out;
+}
+
+static void write_invalid_filename(git_repository *repo, const char *fn_orig)
+{
git_index *index;
git_oid expected;
+ const git_index_entry *entry;
+ git_buf path = GIT_BUF_INIT;
+ char *fn;
- p_mkdir("read_tree", 0700);
-
- cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
cl_git_pass(git_repository_index(&index, repo));
-
cl_assert(git_index_entrycount(index) == 0);
- cl_git_mkfile("./read_tree/.git/hello", NULL);
+ /*
+ * Sneak a valid path into the index, we'll update it
+ * to an invalid path when we try to write the index.
+ */
+ fn = git__strdup(fn_orig);
+ replace_char(fn, '/', '_');
+
+ git_buf_joinpath(&path, "./invalid", fn);
- cl_git_pass(git_index_add_bypath(index, ".git/hello"));
+ cl_git_mkfile(path.ptr, NULL);
+
+ cl_git_pass(git_index_add_bypath(index, fn));
+
+ cl_assert(entry = git_index_get_bypath(index, fn, 0));
+
+ /* kids, don't try this at home */
+ replace_char((char *)entry->path, '_', '/');
/* write-tree */
cl_git_fail(git_index_write_tree(&expected, index));
+ p_unlink(path.ptr);
+
+ cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
+ git_buf_free(&path);
git_index_free(index);
+ git__free(fn);
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests__write_invalid_filename(void)
+{
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ write_invalid_filename(repo, ".git/hello");
+ write_invalid_filename(repo, ".GIT/hello");
+ write_invalid_filename(repo, ".GiT/hello");
+ write_invalid_filename(repo, "./.git/hello");
+ write_invalid_filename(repo, "./foo");
+ write_invalid_filename(repo, "./bar");
+ write_invalid_filename(repo, "foo/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+void test_index_tests__honors_protect_filesystems(void)
+{
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ write_invalid_filename(repo, ".git./hello");
+ write_invalid_filename(repo, ".git\xe2\x80\xad/hello");
+ write_invalid_filename(repo, "git~1/hello");
+ write_invalid_filename(repo, ".git\xe2\x81\xaf/hello");
+
git_repository_free(repo);
- cl_fixture_cleanup("read_tree");
+ cl_fixture_cleanup("invalid");
}
void test_index_tests__remove_entry(void)
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index 736261b31..b78253dc3 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -162,3 +162,37 @@ void test_network_fetchlocal__multi_remotes(void)
git_remote_free(test);
git_remote_free(test2);
}
+
+static int sideband_cb(const char *str, int len, void *payload)
+{
+ int *count = (int *) payload;
+
+ GIT_UNUSED(str);
+ GIT_UNUSED(len);
+
+ (*count)++;
+ return 0;
+}
+
+void test_network_fetchlocal__call_progress(void)
+{
+ git_repository *repo;
+ git_remote *remote;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ int callcount = 0;
+
+ cl_git_pass(git_repository_init(&repo, "foo.git", true));
+ cl_set_cleanup(cleanup_local_repo, "foo.git");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/heads/*"));
+
+ callbacks.sideband_progress = sideband_cb;
+ callbacks.payload = &callcount;
+ cl_git_pass(git_remote_set_callbacks(remote, &callbacks));
+
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+ cl_assert(callcount != 0);
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+}
diff --git a/tests/object/tree/attributes.c b/tests/object/tree/attributes.c
index 85216cd1b..14f3f89f9 100644
--- a/tests/object/tree/attributes.c
+++ b/tests/object/tree/attributes.c
@@ -1,9 +1,21 @@
#include "clar_libgit2.h"
#include "tree.h"
+static git_repository *repo;
+
static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
+void test_object_tree_attributes__initialize(void)
+{
+ repo = cl_git_sandbox_init("deprecated-mode.git");
+}
+
+void test_object_tree_attributes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
{
git_treebuilder *builder;
@@ -11,7 +23,7 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
cl_git_pass(git_oid_fromstr(&oid, blob_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
@@ -22,7 +34,6 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
{
- git_repository *repo;
git_oid tid;
git_tree *tree;
const git_tree_entry *entry;
@@ -38,7 +49,6 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_an
git_tree_entry_filemode(entry));
git_tree_free(tree);
- git_repository_free(repo);
}
void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
@@ -48,7 +58,7 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
const git_tree_entry *entry;
cl_git_pass(git_oid_fromstr(&bid, blob_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert(
&entry,
@@ -62,25 +72,22 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
{
- git_repository *repo;
git_treebuilder *builder;
git_oid tid, tid2;
git_tree *tree;
const git_tree_entry *entry;
- repo = cl_git_sandbox_init("deprecated-mode.git");
-
cl_git_pass(git_oid_fromstr(&tid, tree_oid));
cl_git_pass(git_tree_lookup(&tree, repo, &tid));
- cl_git_pass(git_treebuilder_create(&builder, tree));
+ cl_git_pass(git_treebuilder_create(&builder, repo, tree));
entry = git_treebuilder_get(builder, "old_mode.txt");
cl_assert_equal_i(
GIT_FILEMODE_BLOB,
git_tree_entry_filemode(entry));
- cl_git_pass(git_treebuilder_write(&tid2, repo, builder));
+ cl_git_pass(git_treebuilder_write(&tid2, builder));
git_treebuilder_free(builder);
git_tree_free(tree);
@@ -91,18 +98,14 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from
git_tree_entry_filemode(entry));
git_tree_free(tree);
- cl_git_sandbox_cleanup();
}
void test_object_tree_attributes__normalize_600(void)
{
git_oid id;
git_tree *tree;
- git_repository *repo;
const git_tree_entry *entry;
- repo = cl_git_sandbox_init("deprecated-mode.git");
-
git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
cl_git_pass(git_tree_lookup(&tree, repo, &id));
@@ -111,5 +114,4 @@ void test_object_tree_attributes__normalize_600(void)
cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
git_tree_free(tree);
- cl_git_sandbox_cleanup();
}
diff --git a/tests/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c
index 1b752acbb..11314ec90 100644
--- a/tests/object/tree/duplicateentries.c
+++ b/tests/object/tree/duplicateentries.c
@@ -57,11 +57,11 @@ static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
{
git_treebuilder *builder;
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, _repo, NULL));
fn(builder);
- cl_git_pass(git_treebuilder_write(out, _repo, builder));
+ cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder);
}
diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c
index ddb62e278..2947ac362 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -35,7 +35,7 @@ void test_object_tree_write__from_memory(void)
* on REPOSITORY_FOLDER.
*/
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
- cl_git_pass(git_treebuilder_create(&builder, tree));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_fail(git_treebuilder_insert(NULL, builder, "",
&bid, GIT_FILEMODE_BLOB));
@@ -53,7 +53,7 @@ void test_object_tree_write__from_memory(void)
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
- cl_git_pass(git_treebuilder_write(&rid, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&rid, builder));
cl_assert(git_oid_cmp(&rid, &id2) == 0);
@@ -75,18 +75,18 @@ void test_object_tree_write__subtree(void)
git_oid_fromstr(&bid, blob_oid);
/* create subtree */
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */
- cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&subtree_id, builder));
git_treebuilder_free(builder);
/* create parent tree */
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
- cl_git_pass(git_treebuilder_create(&builder, tree));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */
- cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&id_hiearar, builder));
git_treebuilder_free(builder);
git_tree_free(tree);
@@ -135,14 +135,14 @@ void test_object_tree_write__sorted_subtrees(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
cl_git_pass(git_treebuilder_insert(NULL,
builder, entries[i].filename, &blank_oid, entries[i].attr));
}
- cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
for (i = 0; i < git_tree_entrycount(tree); i++) {
@@ -192,7 +192,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
@@ -229,7 +229,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
- cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder);
@@ -283,7 +283,7 @@ void test_object_tree_write__filtering(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; _entries[i].filename; ++i)
cl_git_pass(git_treebuilder_insert(NULL,
@@ -310,7 +310,7 @@ void test_object_tree_write__filtering(void)
cl_assert(git_treebuilder_get(builder, "aardvark") == NULL);
cl_assert(git_treebuilder_get(builder, "last") != NULL);
- cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder);
@@ -346,13 +346,13 @@ void test_object_tree_write__cruel_paths(void)
git_oid_fromstr(&bid, blob_oid);
/* create tree */
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB));
count++;
}
- cl_git_pass(git_treebuilder_write(&id, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&id, builder));
git_treebuilder_free(builder);
/* check data is correct */
@@ -374,12 +374,12 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree);
/* let's try longer paths */
- cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &id, GIT_FILEMODE_TREE));
}
- cl_git_pass(git_treebuilder_write(&subid, g_repo, builder));
+ cl_git_pass(git_treebuilder_write(&subid, builder));
git_treebuilder_free(builder);
/* check data is correct */
@@ -400,3 +400,43 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree);
}
+
+void test_object_tree_write__protect_filesystems(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+
+ /* Ensure that (by default) we can write objects with funny names on
+ * platforms that are not affected.
+ */
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
+
+#ifndef GIT_WIN32
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+#endif
+
+#ifndef __APPLE__
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+#endif
+
+ git_treebuilder_free(builder);
+
+ /* Now turn on core.protectHFS and core.protectNTFS and validate that these
+ * paths are rejected.
+ */
+
+ cl_repo_set_bool(g_repo, "core.protectHFS", true);
+ cl_repo_set_bool(g_repo, "core.protectNTFS", true);
+
+ cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+
+ git_treebuilder_free(builder);
+}
diff --git a/tests/path/core.c b/tests/path/core.c
index 45f54df29..5b110f611 100644
--- a/tests/path/core.c
+++ b/tests/path/core.c
@@ -51,3 +51,257 @@ void test_path_core__make_relative(void)
test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND);
test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND);
}
+
+void test_path_core__isvalid_standard(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/file.txt", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.file", 0));
+}
+
+void test_path_core__isvalid_empty_dir_component(void)
+{
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo//bar", 0));
+
+ /* leading slash */
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "/", 0));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo", 0));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo/bar", 0));
+
+ /* trailing slash */
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/", 0));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/", 0));
+}
+
+void test_path_core__isvalid_dot_and_dotdot(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "..", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/..", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", GIT_PATH_REJECT_TRAVERSAL));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "..", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/..", GIT_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", GIT_PATH_REJECT_TRAVERSAL));
+}
+
+void test_path_core__isvalid_dot_git(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git/foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git/bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "!git/bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.tig", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig/bar", 0));
+}
+
+void test_path_core__isvalid_backslash(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo\\file.txt", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\file.txt", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo\\file.txt", GIT_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\file.txt", GIT_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\", GIT_PATH_REJECT_BACKSLASH));
+}
+
+void test_path_core__isvalid_trailing_dot(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo...", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo./bar", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo.", GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo...", GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar.", GIT_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo./bar", GIT_PATH_REJECT_TRAILING_DOT));
+}
+
+void test_path_core__isvalid_trailing_space(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, " ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo /bar", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar ", GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, " ", GIT_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo /bar", GIT_PATH_REJECT_TRAILING_SPACE));
+}
+
+void test_path_core__isvalid_trailing_colon(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar:", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ":", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:/bar", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:", GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar:", GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ":", GIT_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:/bar", GIT_PATH_REJECT_TRAILING_COLON));
+}
+
+void test_path_core__isvalid_dotgit_ntfs(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.. .", 0));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1 ", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.. .", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git ", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.. .", GIT_PATH_REJECT_DOT_GIT_NTFS));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1 ", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.", GIT_PATH_REJECT_DOT_GIT_NTFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.. .", GIT_PATH_REJECT_DOT_GIT_NTFS));
+}
+
+void test_path_core__isvalid_dos_paths(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf\\zippy", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:asdf\\foobar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "con", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "prn", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "nul", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf\\zippy", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:asdf\\foobar", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "con", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "prn", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "nul", GIT_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "auxn", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "aux\\foo", GIT_PATH_REJECT_DOS_PATHS));
+}
+
+void test_path_core__isvalid_dos_paths_withnum(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf\\zippy", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:asdf\\foobar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt1", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf\\zippy", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:asdf\\foobar", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "com1/foo", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "lpt1", GIT_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "comn", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt0", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt10", GIT_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "lptn", GIT_PATH_REJECT_DOS_PATHS));
+}
+
+void test_path_core__isvalid_nt_chars(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\001foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\037bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf<bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf>foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf:foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\"bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf|foo", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf?bar", 0));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf*bar", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\001foo", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\037bar", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf<bar", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf>foo", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf:foo", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\"bar", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf|foo", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf?bar", GIT_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf*bar", GIT_PATH_REJECT_NT_CHARS));
+}
+
+void test_path_core__isvalid_dotgit_with_hfs_ignorables(void)
+{
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".g\xe2\x80\x8eIt", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, ".\xe2\x80\x8fgIt", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xaa.gIt", GIT_PATH_REJECT_DOT_GIT_HFS));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xab.\xe2\x80\xacG\xe2\x80\xadI\xe2\x80\xaet", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xab.\xe2\x80\xaaG\xe2\x81\xabI\xe2\x80\xact", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xad.\xe2\x80\xaeG\xef\xbb\xbfIT", GIT_PATH_REJECT_DOT_GIT_HFS));
+
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".g", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, " .git", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "..git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT.", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2\x80It", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".\xe2gIt", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "\xe2\x80\xaa.gi", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2i\x80T\x8e", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", GIT_PATH_REJECT_DOT_GIT_HFS));
+ cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", GIT_PATH_REJECT_DOT_GIT_HFS));
+}
diff --git a/tests/path/win32.c b/tests/path/win32.c
new file mode 100644
index 000000000..22742f82d
--- /dev/null
+++ b/tests/path/win32.c
@@ -0,0 +1,214 @@
+
+#include "clar_libgit2.h"
+#include "path.h"
+
+#ifdef GIT_WIN32
+#include "win32/path_w32.h"
+#endif
+
+void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected)
+{
+#ifdef GIT_WIN32
+ git_win32_path path_utf16;
+ int path_utf16len;
+
+ cl_assert((path_utf16len = git_win32_path_from_utf8(path_utf16, utf8_in)) >= 0);
+ cl_assert_equal_wcs(utf16_expected, path_utf16);
+ cl_assert_equal_i(wcslen(utf16_expected), path_utf16len);
+#else
+ GIT_UNUSED(utf8_in);
+ GIT_UNUSED(utf16_expected);
+#endif
+}
+
+void test_path_win32__utf8_to_utf16(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("c:\\", L"\\\\?\\c:\\");
+ test_utf8_to_utf16("C:/", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("c:/", L"\\\\?\\c:\\");
+#endif
+}
+
+void test_path_win32__removes_trailing_slash(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\Foo\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo/", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo///", L"\\\\?\\C:\\Foo");
+#endif
+}
+
+void test_path_win32__squashes_multiple_slashes(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\\\Foo\\Bar\\\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C://Foo/Bar///Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+#endif
+}
+
+void test_path_win32__unc(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path");
+ test_utf8_to_utf16("//server/git/style/unc/path", L"\\\\?\\UNC\\server\\git\\style\\unc\\path");
+#endif
+}
+
+void test_path_win32__honors_max_path(void)
+{
+#ifdef GIT_WIN32
+ git_win32_path path_utf16;
+
+ test_utf8_to_utf16("C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
+ L"\\\\?\\C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
+ test_utf8_to_utf16("\\\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
+ L"\\\\?\\UNC\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
+
+ cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 260 chars and is sadly too long for windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
+ cl_check_fail(git_win32_path_from_utf8(path_utf16, "\\\\unc\\paths are also bound by 260 character restrictions\\including the server name portion\\bcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
+#endif
+}
+
+void test_path_win32__dot_and_dotdot(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\Foo\\..\\Foobar", L"\\\\?\\C:\\Foobar");
+ test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar", L"\\\\?\\C:\\Foo\\Foobar");
+ test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar\\..", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foobar\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar", L"\\\\?\\C:\\Foo\\Foobar");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar/../Asdf/", L"\\\\?\\C:\\Foo\\Asdf");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar/..", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo/..", L"\\\\?\\C:\\");
+
+ test_utf8_to_utf16("C:\\Foo\\Bar\\.\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:\\.\\Foo\\.\\Bar\\.\\Foobar\\.\\", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:/Foo/Bar/./Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:/Foo/../Bar/./Foobar/../", L"\\\\?\\C:\\Bar");
+
+ test_utf8_to_utf16("C:\\Foo\\..\\..\\Bar", L"\\\\?\\C:\\Bar");
+#endif
+}
+
+void test_path_win32__absolute_from_no_drive_letter(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
+ test_utf8_to_utf16("/Foo/Bar", L"\\\\?\\C:\\Foo\\Bar");
+#endif
+}
+
+void test_path_win32__absolute_from_relative(void)
+{
+#ifdef GIT_WIN32
+ char cwd_backup[MAX_PATH];
+
+ cl_must_pass(p_getcwd(cwd_backup, MAX_PATH));
+ cl_must_pass(p_chdir("C:/"));
+
+ test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("..\\..\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("Foo\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("Foo\\..\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("", L"\\\\?\\C:\\");
+
+ cl_must_pass(p_chdir("C:/Windows"));
+
+ test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Windows\\Foo");
+ test_utf8_to_utf16("Foo\\Bar", L"\\\\?\\C:\\Windows\\Foo\\Bar");
+ test_utf8_to_utf16("..\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("Foo\\..\\Bar", L"\\\\?\\C:\\Windows\\Bar");
+ test_utf8_to_utf16("", L"\\\\?\\C:\\Windows");
+
+ cl_must_pass(p_chdir(cwd_backup));
+#endif
+}
+
+void test_canonicalize(const wchar_t *in, const wchar_t *expected)
+{
+#ifdef GIT_WIN32
+ git_win32_path canonical;
+
+ cl_assert(wcslen(in) < MAX_PATH);
+ wcscpy(canonical, in);
+
+ cl_must_pass(git_win32_path_canonicalize(canonical));
+ cl_assert_equal_wcs(expected, canonical);
+#else
+ GIT_UNUSED(in);
+ GIT_UNUSED(expected);
+#endif
+}
+
+void test_path_win32__canonicalize(void)
+{
+#ifdef GIT_WIN32
+ test_canonicalize(L"C:\\Foo\\Bar", L"C:\\Foo\\Bar");
+ test_canonicalize(L"C:\\Foo\\", L"C:\\Foo");
+ test_canonicalize(L"C:\\Foo\\\\", L"C:\\Foo");
+ test_canonicalize(L"C:\\Foo\\..\\Bar", L"C:\\Bar");
+ test_canonicalize(L"C:\\Foo\\..\\..\\Bar", L"C:\\Bar");
+ test_canonicalize(L"C:\\Foo\\..\\..\\..\\..\\", L"C:\\");
+ test_canonicalize(L"C:/Foo/Bar", L"C:\\Foo\\Bar");
+ test_canonicalize(L"C:/", L"C:\\");
+
+ test_canonicalize(L"Foo\\\\Bar\\\\Asdf\\\\", L"Foo\\Bar\\Asdf");
+ test_canonicalize(L"Foo\\\\Bar\\\\..\\\\Asdf\\", L"Foo\\Asdf");
+ test_canonicalize(L"Foo\\\\Bar\\\\.\\\\Asdf\\", L"Foo\\Bar\\Asdf");
+ test_canonicalize(L"Foo\\\\..\\Bar\\\\.\\\\Asdf\\", L"Bar\\Asdf");
+ test_canonicalize(L"\\", L"");
+ test_canonicalize(L"", L"");
+ test_canonicalize(L"Foo\\..\\..\\..\\..", L"");
+ test_canonicalize(L"..\\..\\..\\..", L"");
+ test_canonicalize(L"\\..\\..\\..\\..", L"");
+
+ test_canonicalize(L"\\\\?\\C:\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
+ test_canonicalize(L"\\\\?\\C:\\Foo\\Bar\\", L"\\\\?\\C:\\Foo\\Bar");
+ test_canonicalize(L"\\\\?\\C:\\\\Foo\\.\\Bar\\\\..\\", L"\\\\?\\C:\\Foo");
+ test_canonicalize(L"\\\\?\\C:\\\\", L"\\\\?\\C:\\");
+ test_canonicalize(L"//?/C:/", L"\\\\?\\C:\\");
+ test_canonicalize(L"//?/C:/../../Foo/", L"\\\\?\\C:\\Foo");
+ test_canonicalize(L"//?/C:/Foo/../../", L"\\\\?\\C:\\");
+
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\..\\..\\..\\..\\share\\", L"\\\\?\\UNC\\server\\share");
+
+ test_canonicalize(L"\\\\server\\share", L"\\\\server\\share");
+ test_canonicalize(L"\\\\server\\share\\", L"\\\\server\\share");
+ test_canonicalize(L"\\\\server\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
+ test_canonicalize(L"\\\\server\\\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
+ test_canonicalize(L"\\\\server\\share\\..\\foo", L"\\\\server\\foo");
+ test_canonicalize(L"\\\\server\\..\\..\\share\\.\\foo", L"\\\\server\\share\\foo");
+#endif
+}
+
+void test_path_win32__8dot3_name(void)
+{
+#ifdef GIT_WIN32
+ char *shortname;
+
+ /* Some guaranteed short names */
+ cl_assert_equal_s("PROGRA~1", (shortname = git_win32_path_8dot3_name("C:\\Program Files")));
+ git__free(shortname);
+
+ cl_assert_equal_s("WINDOWS", (shortname = git_win32_path_8dot3_name("C:\\WINDOWS")));
+ git__free(shortname);
+
+ /* Create some predictible short names */
+ cl_must_pass(p_mkdir(".foo", 0777));
+ cl_assert_equal_s("FOO~1", (shortname = git_win32_path_8dot3_name(".foo")));
+ git__free(shortname);
+
+ cl_git_write2file("bar~1", "foobar\n", 7, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_mkdir(".bar", 0777));
+ cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar")));
+ git__free(shortname);
+#endif
+}
diff --git a/tests/refs/create.c b/tests/refs/create.c
index 8e4d8d70b..3af7c1d15 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -151,13 +151,11 @@ void test_refs_create__propagate_eexists(void)
cl_assert(error == GIT_EEXISTS);
}
-void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
+static void test_invalid_name(const char *name)
{
git_reference *new_reference;
git_oid id;
- const char *name = "refs/heads/inv@{id";
-
git_oid_fromstr(&id, current_master_tip);
cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create(
@@ -166,3 +164,47 @@ void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALI
cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(
&new_reference, g_repo, name, current_head_target, 0, NULL, NULL));
}
+
+void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ test_invalid_name("refs/heads/inv@{id");
+ test_invalid_name("refs/heads/back\\slash");
+
+ test_invalid_name("refs/heads/foo ");
+ test_invalid_name("refs/heads/foo /bar");
+ test_invalid_name("refs/heads/com1:bar/foo");
+
+ test_invalid_name("refs/heads/e:");
+ test_invalid_name("refs/heads/c:/foo");
+
+ test_invalid_name("refs/heads/foo.");
+}
+
+static void test_win32_name(const char *name)
+{
+ git_reference *new_reference = NULL;
+ git_oid id;
+ int ret;
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ ret = git_reference_create(&new_reference, g_repo, name, &id, 0, NULL, NULL);
+
+#ifdef GIT_WIN32
+ cl_assert_equal_i(GIT_EINVALIDSPEC, ret);
+#else
+ cl_git_pass(ret);
+#endif
+
+ git_reference_free(new_reference);
+}
+
+void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void)
+{
+ test_win32_name("refs/heads/foo./bar");
+
+ test_win32_name("refs/heads/aux");
+ test_win32_name("refs/heads/aux.foo/bar");
+
+ test_win32_name("refs/heads/com1");
+}
diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c
index fb5561bc2..0e8793d99 100644
--- a/tests/repo/iterator.c
+++ b/tests/repo/iterator.c
@@ -427,7 +427,7 @@ static void build_test_tree(
git_buf name = GIT_BUF_INIT;
va_list arglist;
- cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */
+ cl_git_pass(git_treebuilder_create(&builder, repo, NULL)); /* start builder */
va_start(arglist, fmt);
while (*scan) {
@@ -451,7 +451,7 @@ static void build_test_tree(
}
va_end(arglist);
- cl_git_pass(git_treebuilder_write(out, repo, builder));
+ cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder);
git_buf_free(&name);
diff --git a/tests/resources/nasty/.gitted/HEAD b/tests/resources/nasty/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/nasty/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/nasty/.gitted/index b/tests/resources/nasty/.gitted/index
new file mode 100644
index 000000000..782a50d0a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/index
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b b/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b
new file mode 100644
index 000000000..e7cd63a28
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b
@@ -0,0 +1,3 @@
+x]
+!{vw ^Gb#8F,Ao|j)7"Aڠji&.(qIgvBY=-5ײ4'+~m
+l :;9.w@ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a b/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a
new file mode 100644
index 000000000..7f8722e78
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115 b/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115
new file mode 100644
index 000000000..688b970c2
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d b/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d
new file mode 100644
index 000000000..432160104
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631 b/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631
new file mode 100644
index 000000000..7738fc85d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2 b/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2
new file mode 100644
index 000000000..d59836e9c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390 b/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390
new file mode 100644
index 000000000..b06361552
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c b/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c
new file mode 100644
index 000000000..d0433a0d5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1 b/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1
new file mode 100644
index 000000000..9d14298d5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406 b/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406
new file mode 100644
index 000000000..fb03b26b0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 b/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97
new file mode 100644
index 000000000..95bc4c889
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97
@@ -0,0 +1 @@
+xQ D{P1x`"%E y/ %[ծUfQrv-)oXMGK9>F;ů #F3+qΈ˝VA \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1 b/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1
new file mode 100644
index 000000000..ea54830c1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 b/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2
new file mode 100644
index 000000000..a7f3683e4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2
@@ -0,0 +1,2 @@
+x10 Es
+H(iںbae&q(5BܞpԺ*`wژѓ3C”1epB> HKzSKp+R7ys]cMӌK.{WsM?P)“|? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 b/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107
new file mode 100644
index 000000000..f82b82be7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107
@@ -0,0 +1,2 @@
+x=!9&[+/gckW|/Q
+ gDd?*kRҋ+5wl+NO8㠿u[jԩ)Q>Q/q?Pc=?q \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f b/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f
new file mode 100644
index 000000000..46ed5c1e0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/1b/31d55e0c53efbea6c670ece9057b76b5822eea b/tests/resources/nasty/.gitted/objects/1b/31d55e0c53efbea6c670ece9057b76b5822eea
new file mode 100644
index 000000000..20ede1bab
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/1b/31d55e0c53efbea6c670ece9057b76b5822eea
@@ -0,0 +1,2 @@
+xa
+!)Z|t"O' >1׈}ݠ5 ̄Zʭî7ffV)JC9Y%URN~fcgsmp/"\s-k}/M q쥖R :;9#.wmz@ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 b/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32
new file mode 100644
index 000000000..e25f153f4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32
@@ -0,0 +1,2 @@
+xQ D{eaiI(`"%E y/%[iծeqbDd6nBZ<OQHz-
+t55O:ů|ZGFkx\@| \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b b/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b
new file mode 100644
index 000000000..fc11c20d5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7 b/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7
new file mode 100644
index 000000000..6e1a85c08
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7
@@ -0,0 +1 @@
+xQ D{ 1x`"%E y/ %[]1-^#YB^m NҎ&RnR3,Rp\g!vI6 $Th=Di?oq^x@ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5 b/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5
new file mode 100644
index 000000000..10b555b33
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf b/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf
new file mode 100644
index 000000000..b286daa0c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a b/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a
new file mode 100644
index 000000000..5d47d827e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3 b/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3
new file mode 100644
index 000000000..b8633de0a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3
@@ -0,0 +1 @@
+x;0}푐q !Z*.ZIa6!nS=M&QZ"x:z-aOؠB?cV"x-c guK:>%P~!Gq?PC=? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40 b/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40
new file mode 100644
index 000000000..a911c3ca8
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a b/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a
new file mode 100644
index 000000000..3854748c9
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a
@@ -0,0 +1,2 @@
+x=
+B1s& b#b.xq!FQS|3Ekup;\&甸p9X8bz &? NʍaZ>{-~iSKD֢2.dΤ? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c b/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c
new file mode 100644
index 000000000..5adcd1446
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c
@@ -0,0 +1,2 @@
+x10 Es
+Hm&bae&(1Bܞp) !ٱ#t)Y;#z4U*\қj*4a=D)'F;هg쿚/+5@O c? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15 b/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15
new file mode 100644
index 000000000..4eaaa0cd7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd b/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd
new file mode 100644
index 000000000..c81b0e67a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054 b/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054
new file mode 100644
index 000000000..6d1f52df9
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 b/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62
new file mode 100644
index 000000000..2a54fe205
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62
@@ -0,0 +1,3 @@
+x]
+0})6? i!i 77m`Ɨo NVE#^ 8@ r\̬Fx-
+t55'+~m%$i;i#Q!Ny?gފA \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 b/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640
new file mode 100644
index 000000000..fdfe6eb37
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640
@@ -0,0 +1,3 @@
+x]
+0})ݤi%i 77m&o Ȫ]^ˌz"ѓ1bpJs|J
+N~m%FFcWNy?5rw[z of@ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073 b/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073
new file mode 100644
index 000000000..ffd9bfd36
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6 b/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6
new file mode 100644
index 000000000..fa990d408
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5 b/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5
new file mode 100644
index 000000000..c23f81597
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73 b/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73
new file mode 100644
index 000000000..6d7d9f500
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad b/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad
new file mode 100644
index 000000000..121277fdf
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba b/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba
new file mode 100644
index 000000000..8172b7f0a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a b/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a
new file mode 100644
index 000000000..9ed35d78a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 b/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2
new file mode 100644
index 000000000..0c3ea2694
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2
@@ -0,0 +1,2 @@
+x1!E9&X!1 +WMDF\9BMCc—2WpaRԸsCz3Yc5
+n<Q/? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d b/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d
new file mode 100644
index 000000000..17dec5920
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d b/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d
new file mode 100644
index 000000000..f7be9ab39
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a b/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a
new file mode 100644
index 000000000..5964a27d1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3 b/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3
new file mode 100644
index 000000000..d2074aaed
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa b/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa
new file mode 100644
index 000000000..ad272511e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230 b/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230
new file mode 100644
index 000000000..c28ad0a63
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51 b/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51
new file mode 100644
index 000000000..16ea98e26
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070 b/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070
new file mode 100644
index 000000000..432eea283
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629 b/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629
new file mode 100644
index 000000000..6f552e5c3
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf b/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
new file mode 100644
index 000000000..bba2035da
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
@@ -0,0 +1,2 @@
+x]
+!{vw ^F^ZAm+9cDϢv9rNz#&:Έِք)TVzz-Gϵ9>}p͵lu I'_B-G@SR*{9gNΰo5A) \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae b/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae
new file mode 100644
index 000000000..6f3484c1a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e b/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
new file mode 100644
index 000000000..f802e5af7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
@@ -0,0 +1,2 @@
+xK
+1D]}|:c7zt` DzbE"ِ$h08ς"QK[aIp){6V`̌HV{ՠ˟3| kyMoA/ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 b/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0
new file mode 100644
index 000000000..d7147fb1c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0
@@ -0,0 +1,3 @@
+xQ
+1 D)rdZBЭ7o%k
+vӪ*iԣ8kw`y(!~T*UJ^'[h%&H8#ny?gkz o!A2 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd b/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd
new file mode 100644
index 000000000..475d26b2f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 b/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29
new file mode 100644
index 000000000..57419bc77
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29
@@ -0,0 +1 @@
+x;0sPKRqVRE"1W`)$uU0aich0lAWG1&,;deF襋47 E&8qukjICzc8؏UaѦI|\@o \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68 b/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68
new file mode 100644
index 000000000..ff1d33e5c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 b/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911
new file mode 100644
index 000000000..aa24a8fc7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911
@@ -0,0 +1,3 @@
+x=
+1Fs5 bckf' EIF>^=Z79N;i9[|
+h^P+ 3dƖrS.uƝ6a? Ԑ+u.HDY2@% \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d b/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d
new file mode 100644
index 000000000..2cf9535ae
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca b/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca
new file mode 100644
index 000000000..2e36dcae7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9 b/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9
new file mode 100644
index 000000000..75fa458e7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c b/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c
new file mode 100644
index 000000000..9e270bfbc
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275 b/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275
new file mode 100644
index 000000000..b2e0eda1a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05 b/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05
new file mode 100644
index 000000000..6cee4f9d8
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803 b/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803
new file mode 100644
index 000000000..00ab02c21
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 b/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569
new file mode 100644
index 000000000..af02c6b9b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569
@@ -0,0 +1,2 @@
+xK
+1D]}|"n<^`;!@&"(]{P%[fjsb0j4GKYSײ!tI6pG K#괟猺ܧ57w@ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e b/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e
new file mode 100644
index 000000000..939cf5576
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 b/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172
new file mode 100644
index 000000000..b43d3f165
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172
@@ -0,0 +1,2 @@
+x=
+1`=d'n6"6V^ d87^W=n3@yĸ^{Ҙb0FhYMjr/)߮EpJCRиsCzq1%ثits]>P]?1 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99 b/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99
new file mode 100644
index 000000000..1d763482f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee b/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee
new file mode 100644
index 000000000..1b79b342c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd b/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd
new file mode 100644
index 000000000..85ddc7f9b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd
@@ -0,0 +1,3 @@
+x=
+1FsZy1%[Y+W|/qN<KD٠=8ŒCI
+_R5epaRԸ" g|jAݰ*&P+? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1 b/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1
new file mode 100644
index 000000000..732474aef
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1
@@ -0,0 +1,2 @@
+xOI
+1W]t#"^$3LC&͈?N@~*30r`؛S:f4#Ǒ8W-fORVYؕO I 0X}5jxZ`>ޛE \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6 b/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6
new file mode 100644
index 000000000..51ad3880e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 b/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195
new file mode 100644
index 000000000..eb5acc34b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195
@@ -0,0 +1,3 @@
+x;
+B1E*$Zd+Hވ{< 2(v<jU5EF({H:7p)4
+pֹ8h; :"drv\ ᙸ}?F \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4 b/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4
new file mode 100644
index 000000000..e458bf4d2
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4
@@ -0,0 +1,2 @@
+xQ
+B!Ev35!VPq 3gj)ҩMo (3Ț=G2UIGϵ9>}p͵uI'_#ȝ4NYmŠ3rk~Mo"A< \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247 b/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247
new file mode 100644
index 000000000..57329de37
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f b/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f
new file mode 100644
index 000000000..de34bd430
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f
@@ -0,0 +1,2 @@
+xK
+1D]}tf&q m@&"(]{P%k UbȑR28/'\B ƓuLm)I5y)y-wItKN4zZuϛ9N7ZmA \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 b/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4
new file mode 100644
index 000000000..f365908e0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4
@@ -0,0 +1,2 @@
+xK
+1D]}Dx@&i` d" UUr@^nzc-V&iK4xAhP{MA68gh ιaǃ~ҁצXH$aQ :;9#N@W \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 b/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691
new file mode 100644
index 000000000..d8c237946
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691
@@ -0,0 +1,2 @@
+xQ
+1 D)r.,' lЭ7o=MUU2c$$.sxl՚hT8.X8rY;~ů R# M3tkz o{@8 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece b/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece
new file mode 100644
index 000000000..a9b181815
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e b/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e
new file mode 100644
index 000000000..14144d736
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 b/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885
new file mode 100644
index 000000000..6e61c0692
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885
@@ -0,0 +1,2 @@
+xK
+1D]}n z|:D02FރPKv +71Ii?GJ^Zo #r$smpO"\r-kÞ#ůM"cAaQ :;9#7(AD \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5 b/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5
new file mode 100644
index 000000000..b886096ce
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7 b/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7
new file mode 100644
index 000000000..d128a9498
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe b/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe
new file mode 100644
index 000000000..7357306c6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c b/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c
new file mode 100644
index 000000000..c3580d334
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615 b/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
new file mode 100644
index 000000000..890324e6c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8 b/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8
new file mode 100644
index 000000000..c8d38ca46
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8
Binary files differ
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path
new file mode 100644
index 000000000..06132bc80
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path
@@ -0,0 +1 @@
+0228b21d477f67b9f7720565da9e760b84c8b85b
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path
new file mode 100644
index 000000000..fd12c3ec5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path
@@ -0,0 +1 @@
+e2377bdbc93b30a34ed5deefedded89b947ff8f4
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path
new file mode 100644
index 000000000..1f9b2d4a1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path
@@ -0,0 +1 @@
+4aa347c8bb0456230f43f34833c97b9f52c40f62
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree
new file mode 100644
index 000000000..dd9a6c0f7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree
@@ -0,0 +1 @@
+8bcbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_colon b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon
new file mode 100644
index 000000000..39052d99a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon
@@ -0,0 +1 @@
+4414ac920acabc3eb00e3cf9375eeb0cb6859c15
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff
new file mode 100644
index 000000000..a3bc39f66
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff
@@ -0,0 +1 @@
+ccbbfdb796f9b03298f5c7225e8f830784e1a3b1
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_dot b/tests/resources/nasty/.gitted/refs/heads/dot_git_dot
new file mode 100644
index 000000000..b20a1e0ac
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_dot
@@ -0,0 +1 @@
+26b665c162f67acae67779445f3c7b9782b0a6d7
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_path b/tests/resources/nasty/.gitted/refs/heads/dot_path
new file mode 100644
index 000000000..b3c7ab682
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_path
@@ -0,0 +1 @@
+bf7ab4723fcc57ecc7fceccf591d6c4773491569
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_path_two b/tests/resources/nasty/.gitted/refs/heads/dot_path_two
new file mode 100644
index 000000000..515e983c5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_path_two
@@ -0,0 +1 @@
+debdc4a004fda6141a17d9c297617be70d40248f
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_tree b/tests/resources/nasty/.gitted/refs/heads/dot_tree
new file mode 100644
index 000000000..cf95837cc
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_tree
@@ -0,0 +1 @@
+697dc3d723a018538eb819d5db2035c15109af73
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path
new file mode 100644
index 000000000..6e4344dd6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path
@@ -0,0 +1 @@
+099ed86cb8501ae483b1855c351fe1a506ac9631
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path
new file mode 100644
index 000000000..58227911e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path
@@ -0,0 +1 @@
+e87caf56c91ab8d14e4ee8eb56308533503d1885
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree
new file mode 100644
index 000000000..dfb7a1ab0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree
@@ -0,0 +1 @@
+39fb3af508440cf970b92767f6d081c811574d2a
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path
new file mode 100644
index 000000000..6a24cd70e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path
@@ -0,0 +1 @@
+d2eb26d4938550487de59a017a7bfee8ca46b5f4
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path
new file mode 100644
index 000000000..4d79b3bb6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path
@@ -0,0 +1 @@
+1212c12915820e1ad523b6305c0dcdefea8b7e97
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree
new file mode 100644
index 000000000..6ae117ee9
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree
@@ -0,0 +1 @@
+1e3c845808fa5883aa4bcf2f882172edb72a7a32
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_path
new file mode 100644
index 000000000..185e13b11
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_path
@@ -0,0 +1 @@
+91602c85bb50dd834205edd30435b77d5bb9ccf0
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_tree b/tests/resources/nasty/.gitted/refs/heads/dotdot_tree
new file mode 100644
index 000000000..d30a7b52e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_tree
@@ -0,0 +1 @@
+8f1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path b/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path
new file mode 100644
index 000000000..6e4344dd6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path
@@ -0,0 +1 @@
+099ed86cb8501ae483b1855c351fe1a506ac9631
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
new file mode 100644
index 000000000..dc48bd6fc
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
@@ -0,0 +1 @@
+46fe10fa23259b089ab050788b06df979cd7d054
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
new file mode 100644
index 000000000..b3a972629
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
@@ -0,0 +1 @@
+9ab85e507899c19dca57778c9b6e5f1ec799b911
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
new file mode 100644
index 000000000..edf27988a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
@@ -0,0 +1 @@
+15f7d9f9514eeb65b9588c49b10b1da145a729a2
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
new file mode 100644
index 000000000..c4e682e10
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
@@ -0,0 +1 @@
+c3a70f8a376f17adccfb52b48e2831bfef2a2172
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
new file mode 100644
index 000000000..76a155c20
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
@@ -0,0 +1 @@
+c2a2ddd339574e5cbfd9228be840eb1bf496de4e
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
new file mode 100644
index 000000000..be2f83551
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
@@ -0,0 +1 @@
+712ceb8eb3e57072447715bc4057c57aa50f629a
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
new file mode 100644
index 000000000..3fdeecea6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
@@ -0,0 +1 @@
+3b24e5c751ee9c7c89df32a0d959748aa3d0112c
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
new file mode 100644
index 000000000..2739555f7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
@@ -0,0 +1 @@
+c8f98a1762ec016c30f0d73512df399dedefc3fd
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
new file mode 100644
index 000000000..480832e01
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
@@ -0,0 +1 @@
+35ae236308929a536fb4e852278a9b98c42babb3
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
new file mode 100644
index 000000000..8510ece13
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
@@ -0,0 +1 @@
+96156716851c0afb4702b0d2c4ac8c496a730e29
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
new file mode 100644
index 000000000..754b55edd
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
@@ -0,0 +1 @@
+7a0538bc4e20aecb36ef221f2077eb30ebe0bcb2
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
new file mode 100644
index 000000000..161ebc43b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
@@ -0,0 +1 @@
+1635c47d80914f0abfa43dd4234a948db5bdb107
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
new file mode 100644
index 000000000..f8a5fa3f7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
@@ -0,0 +1 @@
+9e24726d64589ba02430da8cebb5712dad35593d
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
new file mode 100644
index 000000000..ad5ad1d70
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
@@ -0,0 +1 @@
+ce22b3cd9a01efafc370879c1938e0c32fb6f195
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
new file mode 100644
index 000000000..4d10c4009
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
@@ -0,0 +1 @@
+a576a98d3279989226992610372035b76a01a3e9
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
new file mode 100644
index 000000000..a935018fa
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
@@ -0,0 +1 @@
+442894787eddb1e84a952f17a027590e2c6c02cd
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dotgit_path
new file mode 100644
index 000000000..dd71efaa4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_path
@@ -0,0 +1 @@
+5341a7b545d71198b076b8ba3374a75c9a290640
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotgit_tree
new file mode 100644
index 000000000..3b7a08d7c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_tree
@@ -0,0 +1 @@
+6594bdbad86bbc8d3ed0806a23827203fbab56c6
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde1 b/tests/resources/nasty/.gitted/refs/heads/git_tilde1
new file mode 100644
index 000000000..d48a18530
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde1
@@ -0,0 +1 @@
+94f37c29173c8fa45a232b17e745c82132b2fafd
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde2 b/tests/resources/nasty/.gitted/refs/heads/git_tilde2
new file mode 100644
index 000000000..77082e153
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde2
@@ -0,0 +1 @@
+899ff28744bed5bece69c78ba752c7dc3e954629
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde3 b/tests/resources/nasty/.gitted/refs/heads/git_tilde3
new file mode 100644
index 000000000..73022aad6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde3
@@ -0,0 +1 @@
+fa9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
diff --git a/tests/resources/nasty/.gitted/refs/heads/master b/tests/resources/nasty/.gitted/refs/heads/master
new file mode 100644
index 000000000..b19343373
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e399c4fc4c07cb7947d2f3d966bc374df6ccc691