}
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, char *symcache)
{
if (lstat(ce->name, st) < 0) {
if (errno != ENOENT && errno != ENOTDIR)
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;
memset(&(dpath->parent[0]), 0,
sizeof(struct combine_diff_parent)*5);
- changed = check_work_tree_entity(ce, &st, symcache);
+ changed = check_removed(ce, &st, symcache);
if (!changed)
dpath->mode = ce_mode_from_stat(ce, st.st_mode);
else {
if (ce_uptodate(ce))
continue;
- changed = check_work_tree_entity(ce, &st, symcache);
+ changed = check_removed(ce, &st, symcache);
if (changed) {
if (changed < 0) {
perror(ce->name);
continue;
}
changed = ce_match_stat(ce, &st, ce_option);
- if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
- continue;
+ if (!changed) {
+ ce_mark_uptodate(ce);
+ if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+ continue;
+ }
oldmode = ce->ce_mode;
newmode = ce_mode_from_stat(ce, st.st_mode);
diff_change(&revs->diffopt, oldmode, newmode,
if (!cached) {
int changed;
struct stat st;
- changed = check_work_tree_entity(ce, &st, cbdata->symcache);
+ changed = check_removed(ce, &st, cbdata->symcache);
if (changed < 0)
return -1;
else if (changed) {
struct index_state the_index;
-static unsigned int hash_name(const char *name, int namelen)
-{
- unsigned int hash = 0x123;
-
- do {
- unsigned char c = *name++;
- 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;
-}
-
static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
{
- ce->ce_flags &= ~CE_UNHASHED;
istate->cache[nr] = ce;
- if (istate->name_hash_initialized)
- hash_index_entry(istate, ce);
+ add_name_hash(istate, ce);
}
static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
{
struct cache_entry *old = istate->cache[nr];
- remove_index_entry(old);
+ remove_name_hash(old);
set_index_entry(istate, nr, ce);
istate->cache_changed = 1;
}
-int index_name_exists(struct index_state *istate, const char *name, int namelen)
-{
- 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 (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))
- return 1;
- }
- ce = ce->next;
- }
- return 0;
-}
-
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
{
- return (istate->timestamp &&
+ return (!S_ISGITLINK(ce->ce_mode) &&
+ istate->timestamp &&
((unsigned int)istate->timestamp) <= ce->ce_mtime);
}
{
struct cache_entry *ce = istate->cache[pos];
- remove_index_entry(ce);
+ remove_name_hash(ce);
istate->cache_changed = 1;
istate->cache_nr--;
if (pos >= istate->cache_nr)
return pos;
}
+static int different_name(struct cache_entry *ce, struct cache_entry *alias)
+{
+ int len = ce_namelen(ce);
+ return ce_namelen(alias) != len || memcmp(ce->name, alias->name, len);
+}
+
+/*
+ * If we add a filename that aliases in the cache, we will use the
+ * name that we already have - but we don't want to update the same
+ * alias twice, because that implies that there were actually two
+ * different files with aliasing names!
+ *
+ * So we use the CE_ADDED flag to verify that the alias was an old
+ * one before we accept it as
+ */
+static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
+{
+ int len;
+ struct cache_entry *new;
+
+ if (alias->ce_flags & CE_ADDED)
+ die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
+
+ /* Ok, create the new entry using the name of the existing alias */
+ len = ce_namelen(alias);
+ new = xcalloc(1, cache_entry_size(len));
+ memcpy(new->name, alias->name, len);
+ copy_cache_entry(new, ce);
+ free(ce);
+ return new;
+}
+
int add_file_to_index(struct index_state *istate, const char *path, int verbose)
{
- int size, namelen, pos;
+ int size, namelen;
struct stat st;
- struct cache_entry *ce;
+ struct cache_entry *ce, *alias;
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
if (lstat(path, &st))
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
}
- pos = index_name_pos(istate, ce->name, namelen);
- if (0 <= pos &&
- !ce_stage(istate->cache[pos]) &&
- !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
+ alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
+ if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, &st, ce_option)) {
/* Nothing changed, really */
free(ce);
- ce_mark_uptodate(istate->cache[pos]);
+ ce_mark_uptodate(alias);
+ alias->ce_flags |= CE_ADDED;
return 0;
}
-
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
+ if (ignore_case && alias && different_name(ce, alias))
+ ce = create_alias_ce(ce, alias);
+ ce->ce_flags |= CE_ADDED;
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
die("unable to add %s to index",path);
if (verbose)
struct cache_entry *ce = cache[i];
if (ce->ce_flags & CE_REMOVE)
continue;
- if (is_racy_timestamp(istate, ce))
+ if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
ce_smudge_racily_clean_entry(ce);
if (ce_write_entry(&c, newfd, ce) < 0)
return -1;