Make git-add behave more sensibly in a case-insensitive environment
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Sat, 22 Mar 2008 21:22:44 +0000 (14:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Apr 2008 08:22:25 +0000 (01:22 -0700)
This expands on the previous patch, and allows "git add" to sanely handle
a filename that has changed case, keeping the case in the index constant,
and avoiding aliases.

In particular, if you have an index entry called "File", but the
checked-out tree is case-corrupted and has an entry called "file"
instead, doing a

git add .

(or naming "file" explicitly) will automatically notice that we have an
alias, and will replace the name "file" with the existing index
capitalization (ie "File").

However, if we actually have *both* a file called "File" and one called
"file", and they don't have the same lstat() information (ie we're on a
case-sensitive filesystem but have the "core.ignorecase" flag set), we
will error out if we try to add them both.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
cache.h
read-cache.c

diff --git a/cache.h b/cache.h
index 9bce723bb88ba4c97f0c1b80abf090ea82fe919b..81727e4afeba48b73d72f47fd89d823f31c59bb7 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -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)
index 8c57adfa166844ec00904187393d97cc7973dd47..6b7d16c554338169057b89c61229367930d9320c 100644 (file)
@@ -429,6 +429,38 @@ static int index_name_pos_also_unmerged(struct index_state *istate,
        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;
@@ -471,11 +503,14 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
                /* Nothing changed, really */
                free(ce);
                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)