Skip to content

Commit

Permalink
Merge branch 'lt/core-optim'
Browse files Browse the repository at this point in the history
* lt/core-optim:
  Optimize symlink/directory detection
  Avoid some unnecessary lstat() calls
  is_racy_timestamp(): do not check timestamp for gitlinks
  diff-lib.c: rename check_work_tree_entity()
  diff: a submodule not checked out is not modified
  Add t7506 to test submodule related functions for git-status
  t4027: test diff for submodule with empty directory
  Make git-add behave more sensibly in a case-insensitive environment
  When adding files to the index, add support for case-independent matches
  Make unpack-tree update removed files before any updated files
  Make branch merging aware of underlying case-insensitive filsystems
  Add 'core.ignorecase' option
  Make hash_name_lookup able to do case-independent lookups
  Make "index_name_exists()" return the cache_entry it found
  Move name hashing functions into a file of its own
  Make unpack_trees_options bit flags actual bitfields
  • Loading branch information
Junio C Hamano committed May 11, 2008
2 parents dfd1b74 + c40641b commit dccb3a6
Show file tree
Hide file tree
Showing 16 changed files with 393 additions and 169 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ LIB_OBJS += log-tree.o
LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
LIB_OBJS += merge-file.o
LIB_OBJS += name-hash.o
LIB_OBJS += object.o
LIB_OBJS += pack-check.o
LIB_OBJS += pack-revindex.o
Expand Down
2 changes: 1 addition & 1 deletion builtin-apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -2247,7 +2247,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
* In such a case, path "new_name" does not exist as
* far as git is concerned.
*/
if (has_symlink_leading_path(new_name, NULL))
if (has_symlink_leading_path(strlen(new_name), new_name))
return 0;

return error("%s: already exists in working directory", new_name);
Expand Down
6 changes: 4 additions & 2 deletions builtin-commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,11 @@ static void add_remove_files(struct path_list *list)
{
int i;
for (i = 0; i < list->nr; i++) {
struct stat st;
struct path_list_item *p = &(list->items[i]);
if (file_exists(p->path))
add_file_to_cache(p->path, 0);

if (!lstat(p->path, &st))
add_to_cache(p->path, &st, 0);
else
remove_file_from_cache(p->path);
}
Expand Down
2 changes: 1 addition & 1 deletion builtin-read-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static int read_cache_unmerged(void)
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
remove_index_entry(ce);
remove_name_hash(ce);
if (last && !strcmp(ce->name, last->name))
continue;
cache_tree_invalidate_path(active_cache_tree, ce->name);
Expand Down
41 changes: 24 additions & 17 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct cache_entry {
#define CE_UPDATE (0x10000)
#define CE_REMOVE (0x20000)
#define CE_UPTODATE (0x40000)
#define CE_ADDED (0x80000)

#define CE_HASHED (0x100000)
#define CE_UNHASHED (0x200000)
Expand All @@ -153,20 +154,6 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry
dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
}

/*
* We don't actually *remove* it, we can just mark it invalid so that
* we won't find it in lookups.
*
* Not only would we have to search the lists (simple enough), but
* we'd also have to rehash other hash buckets in case this makes the
* hash bucket empty (common). So it's much better to just mark
* it.
*/
static inline void remove_index_entry(struct cache_entry *ce)
{
ce->ce_flags |= CE_UNHASHED;
}

static inline unsigned create_ce_flags(size_t len, unsigned stage)
{
if (len >= CE_NAMEMASK)
Expand Down Expand Up @@ -241,6 +228,23 @@ struct index_state {

extern struct index_state the_index;

/* Name hashing */
extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
/*
* We don't actually *remove* it, we can just mark it invalid so that
* we won't find it in lookups.
*
* Not only would we have to search the lists (simple enough), but
* we'd also have to rehash other hash buckets in case this makes the
* hash bucket empty (common). So it's much better to just mark
* it.
*/
static inline void remove_name_hash(struct cache_entry *ce)
{
ce->ce_flags |= CE_UNHASHED;
}


#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
#define active_cache (the_index.cache)
#define active_nr (the_index.cache_nr)
Expand All @@ -257,11 +261,12 @@ extern struct index_state the_index;
#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))
#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
#endif

enum object_type {
Expand Down Expand Up @@ -351,7 +356,7 @@ extern int write_index(const struct index_state *, int newfd);
extern int discard_index(struct index_state *);
extern int unmerged_index(const struct index_state *);
extern int verify_path(const char *path);
extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
extern int index_name_pos(const struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
Expand All @@ -361,6 +366,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_index_entry_at(struct index_state *, int pos);
extern int remove_file_from_index(struct index_state *, const char *path);
extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
extern int add_file_to_index(struct index_state *, const char *path, int verbose);
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
Expand Down Expand Up @@ -405,6 +411,7 @@ extern int delete_ref(const char *, const unsigned char *sha1);
extern int trust_executable_bit;
extern int quote_path_fully;
extern int has_symlinks;
extern int ignore_case;
extern int assume_unchanged;
extern int prefer_symlink_refs;
extern int log_all_ref_updates;
Expand Down Expand Up @@ -599,7 +606,7 @@ struct checkout {
};

extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
extern int has_symlink_leading_path(const char *name, char *last_symlink);
extern int has_symlink_leading_path(int len, const char *name);

extern struct alternate_object_database {
struct alternate_object_database *next;
Expand Down
5 changes: 5 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}

if (!strcmp(var, "core.ignorecase")) {
ignore_case = git_config_bool(var, value);
return 0;
}

if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;
Expand Down
35 changes: 27 additions & 8 deletions diff-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,22 +337,41 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
}
return run_diff_files(revs, options);
}

/*
* See if work tree has an entity that can be staged. Return 0 if so,
* return 1 if not and return -1 if error.
* Has the work tree entity been removed?
*
* Return 1 if it was removed from the work tree, 0 if an entity to be
* compared with the cache entry ce still exists (the latter includes
* the case where a directory that is not a submodule repository
* exists for ce that is a submodule -- it is a submodule that is not
* checked out). Return negative for an error.
*/
static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache)
static int check_removed(const struct cache_entry *ce, struct stat *st)
{
if (lstat(ce->name, st) < 0) {
if (errno != ENOENT && errno != ENOTDIR)
return -1;
return 1;
}
if (has_symlink_leading_path(ce->name, symcache))
if (has_symlink_leading_path(ce_namelen(ce), ce->name))
return 1;
if (S_ISDIR(st->st_mode)) {
unsigned char sub[20];
if (resolve_gitlink_ref(ce->name, "HEAD", sub))

/*
* If ce is already a gitlink, we can have a plain
* directory (i.e. the submodule is not checked out),
* or a checked out submodule. Either case this is not
* a case where something was removed from the work tree,
* so we will return 0.
*
* Otherwise, if the directory is not a submodule
* repository, that means ce which was a blob turned into
* a directory --- the blob was removed!
*/
if (!S_ISGITLINK(ce->ce_mode) &&
resolve_gitlink_ref(ce->name, "HEAD", sub))
return 1;
}
return 0;
Expand Down Expand Up @@ -402,7 +421,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
memset(&(dpath->parent[0]), 0,
sizeof(struct combine_diff_parent)*5);

changed = check_work_tree_entity(ce, &st, symcache);
changed = check_removed(ce, &st);
if (!changed)
dpath->mode = ce_mode_from_stat(ce, st.st_mode);
else {
Expand Down Expand Up @@ -466,7 +485,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (ce_uptodate(ce))
continue;

changed = check_work_tree_entity(ce, &st, symcache);
changed = check_removed(ce, &st);
if (changed) {
if (changed < 0) {
perror(ce->name);
Expand Down Expand Up @@ -527,7 +546,7 @@ static int get_stat_data(struct cache_entry *ce,
if (!cached) {
int changed;
struct stat st;
changed = check_work_tree_entity(ce, &st, cbdata->symcache);
changed = check_removed(ce, &st);
if (changed < 0)
return -1;
else if (changed) {
Expand Down
2 changes: 1 addition & 1 deletion dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)

struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
{
if (cache_name_exists(pathname, len))
if (cache_name_exists(pathname, len, ignore_case))
return NULL;

ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
Expand Down
1 change: 1 addition & 0 deletions environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
int trust_executable_bit = 1;
int quote_path_fully = 1;
int has_symlinks = 1;
int ignore_case;
int assume_unchanged;
int prefer_symlink_refs;
int is_bare_repository_cfg = -1; /* unspecified */
Expand Down
119 changes: 119 additions & 0 deletions name-hash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* name-hash.c
*
* Hashing names in the index state
*
* Copyright (C) 2008 Linus Torvalds
*/
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"

/*
* This removes bit 5 if bit 6 is set.
*
* That will make US-ASCII characters hash to their upper-case
* equivalent. We could easily do this one whole word at a time,
* but that's for future worries.
*/
static inline unsigned char icase_hash(unsigned char c)
{
return c & ~((c & 0x40) >> 1);
}

static unsigned int hash_name(const char *name, int namelen)
{
unsigned int hash = 0x123;

do {
unsigned char c = *name++;
c = icase_hash(c);
hash = hash*101 + c;
} while (--namelen);
return hash;
}

static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
{
void **pos;
unsigned int hash;

if (ce->ce_flags & CE_HASHED)
return;
ce->ce_flags |= CE_HASHED;
ce->next = NULL;
hash = hash_name(ce->name, ce_namelen(ce));
pos = insert_hash(hash, ce, &istate->name_hash);
if (pos) {
ce->next = *pos;
*pos = ce;
}
}

static void lazy_init_name_hash(struct index_state *istate)
{
int nr;

if (istate->name_hash_initialized)
return;
for (nr = 0; nr < istate->cache_nr; nr++)
hash_index_entry(istate, istate->cache[nr]);
istate->name_hash_initialized = 1;
}

void add_name_hash(struct index_state *istate, struct cache_entry *ce)
{
ce->ce_flags &= ~CE_UNHASHED;
if (istate->name_hash_initialized)
hash_index_entry(istate, ce);
}

static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
{
if (len1 != len2)
return 0;

while (len1) {
unsigned char c1 = *name1++;
unsigned char c2 = *name2++;
len1--;
if (c1 != c2) {
c1 = toupper(c1);
c2 = toupper(c2);
if (c1 != c2)
return 0;
}
}
return 1;
}

static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase)
{
int len = ce_namelen(ce);

/*
* Always do exact compare, even if we want a case-ignoring comparison;
* we do the quick exact one first, because it will be the common case.
*/
if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
return 1;

return icase && slow_same_name(name, namelen, ce->name, len);
}

struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
{
unsigned int hash = hash_name(name, namelen);
struct cache_entry *ce;

lazy_init_name_hash(istate);
ce = lookup_hash(hash, &istate->name_hash);

while (ce) {
if (!(ce->ce_flags & CE_UNHASHED)) {
if (same_name(ce, name, namelen, icase))
return ce;
}
ce = ce->next;
}
return NULL;
}
Loading

0 comments on commit dccb3a6

Please sign in to comment.