diff options
Diffstat (limited to 'src/index.c')
-rw-r--r-- | src/index.c | 155 |
1 files changed, 139 insertions, 16 deletions
diff --git a/src/index.c b/src/index.c index d58968454..7bc5d5b24 100644 --- a/src/index.c +++ b/src/index.c @@ -90,6 +90,7 @@ struct entry_long { struct entry_srch_key { const char *path; + int path_len; int stage; }; @@ -109,28 +110,49 @@ static int index_srch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; const git_index_entry *entry = array_member; - int ret; + int cmp, len1, len2, len; - ret = strcmp(srch_key->path, entry->path); + len1 = srch_key->path_len; + len2 = strlen(entry->path); + len = len1 < len2 ? len1 : len2; - if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY) - ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); + cmp = memcmp(srch_key->path, entry->path, len); + if (cmp) + return cmp; + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; - return ret; + if (srch_key->stage != GIT_INDEX_STAGE_ANY) + return srch_key->stage - GIT_IDXENTRY_STAGE(entry); + + return 0; } static int index_isrch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; const git_index_entry *entry = array_member; - int ret; + int cmp, len1, len2, len; - ret = strcasecmp(srch_key->path, entry->path); + len1 = srch_key->path_len; + len2 = strlen(entry->path); + len = len1 < len2 ? len1 : len2; - if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY) - ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); + cmp = strncasecmp(srch_key->path, entry->path, len); - return ret; + if (cmp) + return cmp; + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + + if (srch_key->stage != GIT_INDEX_STAGE_ANY) + return srch_key->stage - GIT_IDXENTRY_STAGE(entry); + + return 0; } static int index_cmp_path(const void *a, const void *b) @@ -579,7 +601,7 @@ const git_index_entry *git_index_get_bypath( git_vector_sort(&index->entries); - if (git_index__find(&pos, index, path, stage) < 0) { + if (git_index__find(&pos, index, path, strlen(path), stage) < 0) { giterr_set(GITERR_INDEX, "Index does not contain %s", path); return NULL; } @@ -701,6 +723,101 @@ static git_index_entry *index_entry_dup(const git_index_entry *source_entry) return entry; } +static int has_file_name(git_index *index, + const git_index_entry *entry, size_t pos, int ok_to_replace) +{ + int retval = 0; + size_t len = strlen(entry->path); + int stage = GIT_IDXENTRY_STAGE(entry); + const char *name = entry->path; + + while (pos < index->entries.length) { + git_index_entry *p = index->entries.contents[pos++]; + + if (len >= strlen(p->path)) + break; + if (memcmp(name, p->path, len)) + break; + if (GIT_IDXENTRY_STAGE(p) != stage) + continue; + if (p->path[len] != '/') + continue; + retval = -1; + if (!ok_to_replace) + break; + git_vector_remove(&index->entries, --pos); + } + return retval; +} + +/* + * Do we have another file with a pathname that is a proper + * subset of the name we're trying to add? + */ +static int has_dir_name(git_index *index, + const git_index_entry *entry, int ok_to_replace) +{ + int retval = 0; + int stage = GIT_IDXENTRY_STAGE(entry); + const char *name = entry->path; + const char *slash = name + strlen(name); + + for (;;) { + size_t len, position; + + for (;;) { + if (*--slash == '/') + break; + if (slash <= entry->path) + return retval; + } + len = slash - name; + + if (git_index__find(&position, index, name, len, stage) == 0) { + retval = -1; + if (!ok_to_replace) + break; + + git_vector_remove(&index->entries, position); + continue; + } + + /* + * Trivial optimization: if we find an entry that + * already matches the sub-directory, then we know + * we're ok, and we can exit. + */ + while (position < index->entries.length) { + git_index_entry *p = index->entries.contents[position]; + + if ((strlen(p->path) <= len) || + (p->path[len] != '/') || + memcmp(p->path, name, len)) + break; /* not our subdirectory */ + + if (GIT_IDXENTRY_STAGE(p) == stage) + return retval; + + position++; + } + } + return retval; +} + +static int check_file_directory_collision(git_index *index, + git_index_entry *entry, size_t pos, int ok_to_replace) +{ + int retval = has_file_name(index, entry, pos, ok_to_replace); + retval = retval + has_dir_name(index, entry, ok_to_replace); + + if (retval) { + giterr_set(GITERR_INDEX, "'%s' appears as both a file an a directory", entry->path); + return -1; + } + + return 0; +} + static int index_insert(git_index *index, git_index_entry *entry, int replace) { size_t path_length, position; @@ -720,13 +837,16 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace) /* look if an entry with this path already exists */ if (!git_index__find( - &position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) { + &position, index, entry->path, strlen(entry->path), + GIT_IDXENTRY_STAGE(entry))) { existing = (git_index_entry **)&index->entries.contents[position]; - /* update filemode to existing values if stat is not trusted */ entry->mode = index_merge_mode(index, *existing, entry->mode); } + if (check_file_directory_collision(index, entry, position, replace) < 0) + return -1; + /* if replacing is not requested or no existing entry exists, just * insert entry at the end; the index is no longer sorted */ @@ -832,7 +952,7 @@ int git_index_remove(git_index *index, const char *path, int stage) git_vector_sort(&index->entries); - if (git_index__find(&position, index, path, stage) < 0) { + if (git_index__find(&position, index, path, strlen(path), stage) < 0) { giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); return GIT_ENOTFOUND; @@ -889,13 +1009,14 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage) } int git_index__find( - size_t *at_pos, git_index *index, const char *path, int stage) + size_t *at_pos, git_index *index, const char *path, int path_len, int stage) { struct entry_srch_key srch_key; assert(path); srch_key.path = path; + srch_key.path_len = path_len; srch_key.stage = stage; return git_vector_bsearch2( @@ -937,6 +1058,7 @@ size_t git_index__prefix_position(git_index *index, const char *path) size_t pos; srch_key.path = path; + srch_key.path_len = strlen(path); srch_key.stage = 0; git_vector_sort(&index->entries); @@ -1991,6 +2113,7 @@ static int read_tree_cb( struct entry_srch_key skey; skey.path = path.ptr; + skey.path_len = strlen(path.ptr); skey.stage = 0; if (!git_vector_bsearch2( @@ -2109,7 +2232,7 @@ int git_index_add_all( /* skip ignored items that are not already in the index */ if ((flags & GIT_INDEX_ADD_FORCE) == 0 && git_iterator_current_is_ignored(wditer) && - git_index__find(&existing, index, wd->path, 0) < 0) + git_index__find(&existing, index, wd->path, strlen(wd->path), 0) < 0) continue; /* issue notification callback if requested */ |