git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 9/7] Make git-add behave more sensibly in a case-insensitive environment

From
Linus Torvalds <torvalds@linux-foundation.org>
Date
Mar 22, 2008, 21:22 UTC
Message-ID
<alpine.LFD.1.00.0803221419400.3020@woody.linux-foundation.org>
In-Reply-To
<alpine.LFD.1.00.0803221417430.3020@woody.linux-foundation.org>
From: Linus Torvalds <torvalds@woody.linux-foundation.org>
Subject: [PATCH 2/2] Make git-add behave more sensibly in a case-insensitive environment

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>
---

The previous patch handled the "nothing changed" case, this one actually handles the case of the data needing to be updated.

The CE_ADDED flag is an in-memory flag that just protects a single "git add" invocation from changing the same alias twice. That would not be right, but if you do separate

	git add File
	git add file

commands, the second one will happily update the information that the first one added even if it was different - but

	git add File file
would be an error if they don't have the same stat() information.
 cache.h      |    1 +
 read-cache.c |   37 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 37 insertions(+), 1 deletions(-)
diff --git a/cache.h b/cache.h
index 9bce723..81727e4 100644
--- 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)
diff --git a/read-cache.c b/read-cache.c
index 8c57adf..26ed644 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -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)
-- 
1.5.5.rc0.31.gdcfd.dirty
Previous: Linus TorvaldsNext: Junio C Hamano
Message 12 of 29 in “Case-insensitive filesystem support, take 1”
  1. 0/7 Case-insensitive filesystem support, take 1Linus Torvalds, Mar 22, 2008
  2. 1/7 Make unpack_trees_options bit flags actual bitfieldsLinus Torvalds, Mar 22, 2008
  3. 2/7 Move name hashing functions into a file of its ownLinus Torvalds, Mar 22, 2008
  4. 3/7 Make "index_name_exists()" return the cache_entry it foundLinus Torvalds, Mar 22, 2008
  5. 4/7 Make hash_name_lookup able to do case-independent lookupsLinus Torvalds, Mar 22, 2008
  6. 5/7 Add 'core.ignorecase' optionLinus Torvalds, Mar 22, 2008
  7. 6/7 Make branch merging aware of underlying case-insensitive filsystemsLinus Torvalds, Mar 22, 2008
  8. 7/7 Make unpack-tree update removed files before any updated filesLinus Torvalds, Mar 22, 2008
  9. 0/7 Final wordsLinus Torvalds, Mar 22, 2008
  10. Linus TorvaldsMar 22, 2008
  11. 8/7 When adding files to the index, add support for case-independent matchesLinus Torvalds, Mar 22, 2008
  12. 9/7 Make git-add behave more sensibly in a case-insensitive environmentLinus Torvalds, Mar 22, 2008
  13. Junio C HamanoMar 23, 2008
  14. Junio C HamanoMar 23, 2008
  15. Linus TorvaldsMar 23, 2008
  16. Johannes SchindelinMar 22, 2008
  17. Linus TorvaldsMar 22, 2008
  18. Johannes SchindelinMar 22, 2008
  19. t0050: Set core.ignorecase case to activate case insensitivitySteffen Prohaska, Mar 22, 2008
  20. git-init: autodetect core.ignorecaseDmitry Potapov, Mar 25, 2008
  21. Johannes SchindelinMar 25, 2008
  22. git-init: autodetect core.ignorecaseDmitry Potapov, Mar 25, 2008
  23. Dmitry PotapovMar 25, 2008
  24. Dmitry PotapovMar 25, 2008
  25. Linus TorvaldsMar 25, 2008
  26. Dmitry PotapovMar 26, 2008
  27. Linus TorvaldsMar 26, 2008
  28. Derek FawcusMar 25, 2008
  29. Jan HudecMar 25, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.