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

[PATCH v4 3/5] cache: Keep track of conversion mode changes.

From
Henrik Grubbström (Grubba) <grubba@grubba.org>
Date
Jun 1, 2010, 14:41 UTC
Message-ID
<a8e30003e2cb4697316b441d26297eadccfe7f64.1275309129.git.grubba@grubba.org>
In-Reply-To
<cover.1275309129.git.grubba@grubba.org>

The index now keeps track of the conversion mode that was active when the entry was created. This can be used to detect the most common cases of when the conversion mode has changed.

Signed-off-by: Henrik Grubbström <grubba@grubba.org>
---
Rebased on 0ed6711 (aka eb/core-eol).

The conversion mode flags are now based on the EOL_* set of flags, and to avoid code duplication convert.c:get_output_conversion() and convert.c:determine_action() are used to determine the eol conversion mode.

The denormalized eol tests in t0025-crlf-auto.sh have been altered to still test the intended eol conversion properties.

 cache.h              |   11 +++++++++++
 convert.c            |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 read-cache.c         |   37 ++++++++++++++++++++++++++++++++++---
 t/t0025-crlf-auto.sh |   20 ++++++++++++--------
 4 files changed, 103 insertions(+), 11 deletions(-)
diff --git a/cache.h b/cache.h
index 004296d..263f4f3 100644
--- a/cache.h
+++ b/cache.h
@@ -151,10 +151,20 @@ struct cache_entry {
 	unsigned int ce_size;
 	unsigned int ce_flags;
 	unsigned char sha1[20];
+	unsigned int ce_conv_flags;
 	struct cache_entry *next;
 	char name[FLEX_ARRAY]; /* more */
 };
 
+#define CONV_EOL_CRLF		EOL_CRLF
+#define CONV_EOL_LF		EOL_LF
+#define CONV_GIT_LF		0x0004
+#define CONV_GIT_AUTO		0x0008
+#define CONV_IDENT		0x0010
+#define CONV_FILT		0x0020
+#define CONV_MASK		0x003f
+#define CONV_NORM_NEEDED	0x010000
+
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
 #define CE_EXTENDED  (0x4000)
@@ -1016,6 +1026,7 @@ extern void trace_argv_printf(const char **argv, const char *format, ...);
 
 /* convert.c */
 /* returns 1 if *dst was used */
+extern unsigned int git_conv_flags(const char *path);
 extern int convert_to_git(const char *path, const char *src, size_t len,
                           struct strbuf *dst, enum safe_crlf checksafe);
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
diff --git a/convert.c b/convert.c
index 80d80b1..387a7c7 100644
--- a/convert.c
+++ b/convert.c
@@ -681,6 +681,52 @@ enum action determine_action(enum action text_attr, enum eol eol_attr) {
 	return text_attr;
 }
 
+unsigned int git_conv_flags(const char *path)
+{
+	struct git_attr_check check[5];
+	enum action action = CRLF_GUESS;
+	enum eol eol_attr = EOL_UNSET;
+	int ident = 0;
+	unsigned ret = 0;
+	struct convert_driver *drv = NULL;
+
+	setup_convert_check(check);
+	if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
+		action = git_path_check_crlf(path, check + 4);
+		if (action == CRLF_GUESS)
+			action = git_path_check_crlf(path, check + 0);
+		ident = git_path_check_ident(path, check + 1);
+		drv = git_path_check_convert(path, check + 2);
+		eol_attr = git_path_check_eol(path, check + 3);
+	}
+
+	ret = get_output_conversion(action);
+
+	action = determine_action(action, eol_attr);
+
+	switch(action) {
+	case CRLF_BINARY:
+		break;
+	case CRLF_GUESS:
+		if (auto_crlf == AUTO_CRLF_FALSE)
+			break;
+		ret |= CONV_GIT_AUTO;
+		break;
+	case CRLF_AUTO:
+		ret |= CONV_GIT_AUTO|CONV_GIT_LF;
+		break;
+	default:
+		ret |= CONV_GIT_LF;
+		break;
+	}
+
+	if (ident)
+		ret |= CONV_IDENT;
+	if (drv)
+		ret |= CONV_FILT;
+	return ret;
+}
+
 int convert_to_git(const char *path, const char *src, size_t len,
                    struct strbuf *dst, enum safe_crlf checksafe)
 {
diff --git a/read-cache.c b/read-cache.c
index f1f789b..eeda928 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -88,12 +88,38 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 static int ce_compare_data(struct cache_entry *ce, struct stat *st)
 {
 	int match = -1;
-	int fd = open(ce->name, O_RDONLY);
-
+	int fd;
+	unsigned char norm_sha1[20];
+	unsigned int conv_flags = git_conv_flags(ce->name);
+	const unsigned char *cmp_sha1 = ce->sha1;
+
+	if ((conv_flags ^ ce->ce_conv_flags) & CONV_MASK) {
+		if (ce->ce_conv_flags & CONV_NORM_NEEDED) {
+			/* Smudge the entry since it was only correct
+			 * for the old conversion mode. */
+			ce->ce_size = 0;
+		}
+		ce->ce_conv_flags = conv_flags;
+	} else
+		conv_flags = ce->ce_conv_flags & CONV_NORM_NEEDED;
+
+	if (conv_flags) {
+		index_blob(norm_sha1, ce->sha1, 0, ce->name);
+		if (!(conv_flags & CONV_NORM_NEEDED) &&
+		    hashcmp(norm_sha1, ce->sha1)) {
+			ce->ce_conv_flags = conv_flags | CONV_NORM_NEEDED;
+			/* Smudge the entry since we don't know
+			 * the correct value. */
+			ce->ce_size = 0;
+		}
+		cmp_sha1 = norm_sha1;
+	}
+	
+	fd = open(ce->name, O_RDONLY);
 	if (fd >= 0) {
 		unsigned char sha1[20];
 		if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))
-			match = hashcmp(sha1, ce->sha1);
+			match = hashcmp(sha1, cmp_sha1);
 		/* index_fd() closed the file descriptor already */
 	}
 	return match;
@@ -227,6 +253,11 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 		changed |= INODE_CHANGED;
 #endif
 
+	/* ce_size can not be trusted if the conversion mode has changed. */
+	if ((ce->ce_mode & S_IFMT) == S_IFREG &&
+	    ((ce->ce_conv_flags ^ git_conv_flags(ce->name)) & CONV_MASK))
+		return changed;
+
 	if (ce->ce_size != (unsigned int) st->st_size)
 		changed |= DATA_CHANGED;
 
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
index f5f67a6..1bcf3dd 100755
--- a/t/t0025-crlf-auto.sh
+++ b/t/t0025-crlf-auto.sh
@@ -47,9 +47,10 @@ test_expect_success 'crlf=true causes a CRLF file to be normalized' '
 	git read-tree --reset -u HEAD &&
 
 	# Note, "normalized" means that git will normalize it if added
+	echo >>two &&
 	has_cr two &&
-	twodiff=`git diff two` &&
-	test -n "$twodiff"
+	twodiff=`git diff --numstat two | cut -f1` &&
+	test -n "$twodiff" -a "$twodiff" -gt 1
 '
 
 test_expect_success 'text=true causes a CRLF file to be normalized' '
@@ -59,9 +60,10 @@ test_expect_success 'text=true causes a CRLF file to be normalized' '
 	git read-tree --reset -u HEAD &&
 
 	# Note, "normalized" means that git will normalize it if added
+	echo >>two &&
 	has_cr two &&
-	twodiff=`git diff two` &&
-	test -n "$twodiff"
+	twodiff=`git diff --numstat two | cut -f1` &&
+	test -n "$twodiff" -a "$twodiff" -gt 1
 '
 
 test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
@@ -107,11 +109,12 @@ test_expect_success 'autocrlf=true does not normalize CRLF files' '
 	git read-tree --reset -u HEAD &&
 
 	has_cr one &&
+	echo >>two &&
 	has_cr two &&
 	onediff=`git diff one` &&
-	twodiff=`git diff two` &&
+	twodiff=`git diff --numstat two | cut -f1` &&
 	threediff=`git diff three` &&
-	test -z "$onediff" -a -z "$twodiff" -a -z "$threediff"
+	test -z "$onediff" -a -n "$twodiff" -a -z "$threediff" -a "$twodiff" -eq 1
 '
 
 test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
@@ -122,11 +125,12 @@ test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
 	git read-tree --reset -u HEAD &&
 
 	has_cr one &&
+	echo >>two &&
 	has_cr two &&
 	onediff=`git diff one` &&
-	twodiff=`git diff two` &&
+	twodiff=`git diff --numstat two | cut -f1` &&
 	threediff=`git diff three` &&
-	test -z "$onediff" -a -n "$twodiff" -a -z "$threediff"
+	test -z "$onediff" -a -n "$twodiff" -a -z "$threediff" -a "$twodiff" -gt 1
 '
 
 test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
-- 
1.7.0.4.369.g81e89
Previous: Henrik Grubbström (Grubba)Next: Henrik Grubbström (Grubba)
Message 4 of 19 in “Patches to avoid reporting conversion changes.”
  1. 0/5 Patches to avoid reporting conversion changes.Henrik Grubbström (Grubba), Jun 1, 2010
  2. 1/5 sha1_file: Add index_blob().Henrik Grubbström (Grubba), Jun 1, 2010
  3. 2/5 strbuf: Add strbuf_add_uint32().Henrik Grubbström (Grubba), Jun 1, 2010
  4. 3/5 cache: Keep track of conversion mode changes.Henrik Grubbström (Grubba), Jun 1, 2010
  5. 4/5 cache: Add index extension "CONV".Henrik Grubbström (Grubba), Jun 1, 2010
  6. 5/5 t/t0021: Test that conversion changes are detected.Henrik Grubbström (Grubba), Jun 1, 2010
  7. Junio C HamanoJun 2, 2010
  8. Henrik GrubbströmJun 3, 2010
  9. Jonathan NiederJun 4, 2010
  10. Henrik GrubbströmJun 4, 2010
  11. Jonathan NiederJun 4, 2010
  12. Henrik GrubbströmJun 6, 2010
  13. Finn Arne GangstadJun 7, 2010
  14. Henrik GrubbströmJun 7, 2010
  15. Finn Arne GangstadJun 7, 2010
  16. Henrik GrubbströmJun 8, 2010
  17. Finn Arne GangstadJun 9, 2010
  18. Henrik GrubbströmJun 9, 2010
  19. Finn Arne GangstadJun 10, 2010

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.