summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2014-11-25 18:13:00 -0500
committerEdward Thomson <ethomson@microsoft.com>2014-12-16 10:08:53 -0600
commita64119e3963fcd358ba914c9e1a81a890666d15a (patch)
treef81788b6b64bc56b84f59da979895078ec93b246
parent0d388adc86946f3c8cddc2493b470d47a653c4a5 (diff)
downloadlibgit2-a64119e3963fcd358ba914c9e1a81a890666d15a.tar.gz
checkout: disallow bad paths on win32
Disallow: 1. paths with trailing dot 2. paths with trailing space 3. paths with trailing colon 4. paths that are 8.3 short names of .git folders ("GIT~1") 5. paths that are reserved path names (COM1, LPT1, etc). 6. paths with reserved DOS characters (colons, asterisks, etc) These paths would (without \\?\ syntax) be elided to other paths - for example, ".git." would be written as ".git". As a result, writing these paths literally (using \\?\ syntax) makes them hard to operate with from the shell, Windows Explorer or other tools. Disallow these.
-rw-r--r--src/checkout.c28
-rw-r--r--src/index.c119
-rw-r--r--src/path.c148
-rw-r--r--src/path.h38
-rw-r--r--src/refdb_fs.c10
-rw-r--r--src/repository.c25
-rw-r--r--src/repository.h20
-rw-r--r--src/util.c15
-rw-r--r--src/util.h1
-rw-r--r--src/win32/path_w32.c36
-rw-r--r--src/win32/path_w32.h15
-rw-r--r--tests/checkout/nasty.c251
-rw-r--r--tests/path/core.c194
-rw-r--r--tests/path/win32.c24
-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/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/0d/45fb57852c2229346a800bd3fc58e32527a21cbin0 -> 45 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/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/39/fb3af508440cf970b92767f6d081c811574d2a2
-rw-r--r--tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15bin0 -> 135 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/6c/1f5f6fec515d33036b44c596bfae28fc460cbabin0 -> 47 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/a8fe4f10626c50b3a4fd065a4604bafc9f30fabin0 -> 23 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51bin0 -> 45 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/3fdf003bf7261b9155c5748dc0945349b69e68bin0 -> 44 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67cabin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17cbin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c47734915692
-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/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b12
-rw-r--r--tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6bin0 -> 47 bytes
-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/e8/7caf56c91ab8d14e4ee8eb56308533503d18852
-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/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_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
94 files changed, 898 insertions, 90 deletions
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/index.c b/src/index.c
index 644f0c8a7..2db7c8ea5 100644
--- a/src/index.c
+++ b/src/index.c
@@ -762,86 +762,19 @@ void git_index_entry__init_from_stat(
entry->file_size = st->st_size;
}
-/*
- * We fundamentally don't like some paths: we don't want
- * dot or dot-dot anywhere, and for obvious reasons don't
- * want to recurse into ".git" either.
- *
- * Also, we don't want double slashes or slashes at the
- * end that can make pathnames ambiguous.
- */
-static int verify_dotfile(const char *rest)
-{
- /*
- * The first character was '.', but that
- * has already been discarded, we now test
- * the rest.
- */
-
- /* "." is not allowed */
- if (*rest == '\0' || *rest == '/')
- return -1;
-
- switch (*rest) {
- /*
- * ".git" followed by NUL or slash is bad. This
- * shares the path end test with the ".." case.
- */
- case 'g':
- case 'G':
- if (rest[1] != 'i' && rest[1] != 'I')
- break;
- if (rest[2] != 't' && rest[2] != 'T')
- break;
- rest += 2;
- /* fallthrough */
- case '.':
- if (rest[1] == '\0' || rest[1] == '/')
- return -1;
- }
- return 0;
-}
-
-static int verify_component(char c, const char *rest)
-{
- if ((c == '.' && verify_dotfile(rest)) < 0 || c == '/' || c == '\0') {
- giterr_set(GITERR_INDEX, "Invalid path component in index: '%c%s'", c, rest);
- return -1;
- }
- return 0;
-}
-
-static int verify_path(const char *path)
-{
- char c;
-
- /* TODO: should we check this? */
- /*
- if (has_dos_drive_prefix(path))
- return -1;
- */
-
- c = *path++;
- if (verify_component(c, path) < 0)
- return -1;
-
- while ((c = *path++) != '\0') {
- if (c == '/') {
- c = *path++;
- if (verify_component(c, path) < 0)
- return -1;
- }
- }
- return 0;
-}
-
-static int index_entry_create(git_index_entry **out, 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;
- if (verify_path(path) < 0)
+ 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);
@@ -855,7 +788,9 @@ static int index_entry_create(git_index_entry **out, const char *path)
}
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;
@@ -867,7 +802,7 @@ static int index_entry_init(
"Could not initialize index entry. "
"Index is not backed up by an existing repository.");
- if (index_entry_create(&entry, rel_path) < 0)
+ 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 */
@@ -933,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;
@@ -942,7 +880,7 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
return 0;
}
- if (index_entry_create(&entry, src->path) < 0)
+ if (index_entry_create(&entry, repo, src->path) < 0)
return -1;
index_entry_cpy(entry, src);
@@ -1211,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;
@@ -1331,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++) {
@@ -1850,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;
@@ -1914,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;
@@ -2015,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) {
@@ -2376,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;
@@ -2396,7 +2338,7 @@ static int read_tree_cb(
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
return -1;
- if (index_entry_create(&entry, path.ptr) < 0)
+ if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0)
return -1;
entry->mode = tentry->attr;
@@ -2437,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;
@@ -2556,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/path.c b/src/path.c
index effe2fff1..dbe193acb 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,150 @@ 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);
}
+
+GIT_INLINE(bool) verify_shortname(
+ git_repository *repo,
+ const char *component,
+ size_t len)
+{
+ const char *shortname_repo;
+
+ if (len == git_repository__8dot3_default_len &&
+ strncasecmp(git_repository__8dot3_default, component, len) == 0)
+ return false;
+
+ if (repo &&
+ (shortname_repo = git_repository__8dot3_name(repo)) &&
+ shortname_repo != git_repository__8dot3_default &&
+ git__prefixncmp_icase(component, len, shortname_repo) == 0)
+ return false;
+
+ return true;
+}
+
+/* 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 && !git__isdigit(component[3]))
+ return true;
+
+ return (len > last &&
+ component[last] != '.' &&
+ component[last] != ':');
+}
+
+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_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_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;
+
+ 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_GIT_SHORTNAME) &&
+ !verify_shortname(repo, component, len))
+ 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;
+ }
+
+ return true;
+}
+
+bool git_path_isvalid(
+ git_repository *repo,
+ const char *path,
+ unsigned int flags)
+{
+ const char *start, *c;
+
+ 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..a97668a98 100644
--- a/src/path.h
+++ b/src/path.h
@@ -462,4 +462,42 @@ 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_BACKSLASH (1 << 2)
+#define GIT_PATH_REJECT_TRAILING_DOT (1 << 3)
+#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 4)
+#define GIT_PATH_REJECT_TRAILING_COLON (1 << 5)
+#define GIT_PATH_REJECT_DOS_GIT_SHORTNAME (1 << 6)
+#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
+#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
+
+#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_GIT_SHORTNAME | \
+ 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..61d1cd56e 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -712,6 +712,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
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..d9b950aac 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -120,8 +120,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 +177,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/util.c b/src/util.c
index 5c305950f..6b0efbea5 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);
diff --git a/src/util.h b/src/util.h
index 6e57ad8c3..17cc08987 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)
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index f0eacaa63..d66969c4d 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -267,3 +267,39 @@ int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
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
index dc7a68e59..1d10166e8 100644
--- a/src/win32/path_w32.h
+++ b/src/win32/path_w32.h
@@ -26,6 +26,11 @@
*/
#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];
@@ -62,4 +67,14 @@ extern int git_win32_path_canonicalize(git_win32_path path);
*/
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/tests/checkout/nasty.c b/tests/checkout/nasty.c
new file mode 100644
index 000000000..8bc98f3d6
--- /dev/null
+++ b/tests/checkout/nasty.c
@@ -0,0 +1,251 @@
+#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
+}
+
diff --git a/tests/path/core.c b/tests/path/core.c
index 45f54df29..7cc111800 100644
--- a/tests/path/core.c
+++ b/tests/path/core.c
@@ -51,3 +51,197 @@ 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_dos_git_shortname(void)
+{
+ cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1", 0));
+
+ cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1", GIT_PATH_REJECT_DOS_GIT_SHORTNAME));
+}
+
+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(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(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(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(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));
+}
+
+void test_core_path__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));
+}
diff --git a/tests/path/win32.c b/tests/path/win32.c
index ef0b5d2f2..22742f82d 100644
--- a/tests/path/win32.c
+++ b/tests/path/win32.c
@@ -188,3 +188,27 @@ void test_path_win32__canonicalize(void)
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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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_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