{"thread":{"id":"57862","subject":"[PATCH] Prevent git from rehashing 4GBi files","startedAt":"2022-05-07T18:59:28Z","lastAt":"2022-05-07T18:59:28Z","messageCount":1,"participants":["Jason D. Hatton"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"454983","messageId":"20220507185813.1403802-1-jhatton@globalfinishing.com","threadId":"57862","inReplyTo":"philipoakley@iee.email","subject":"[PATCH] Prevent git from rehashing 4GBi files","fromName":"Jason D. Hatton","fromEmail":"jason.hatton@gmail.com","sentAt":"2022-05-07T18:58:14Z","receivedAt":"2022-05-07T18:59:28Z","isPatch":true,"sender":{"key":"jason.hatton@gmail.com","avatar":null},"body":"Git cache stores file sizes using uint32_t. This causes any file\nthat is a multiple of 2^32 to have a cached file size of zero.\nZero is a special value used by racily clean. This causes git to\nrehash every file that is a multiple of 2^32 every time git status\nor git commit is run.\n\nThis patch mitigates the problem by making all files that are a\nmultiple of 2^32 appear to have a size of 1 instead of zero.\n\nSigned-off-by: Jason D. Hatton <jhatton@globalfinishing.com>\n---\n read-cache.c | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 4df97e185e..d80c80ef90 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -163,6 +163,22 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n\n \t\tadd_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n }\n \n+/*\n+ * stat_data only stores file sizes as an unsigned int. Any file that is an\n+ * exact multiple of 4GiB will get a cached file size of zero. Unfortunately,\n+ * this is a special flag used by the racy update logic. Substitute a new file\n+ * size if a non-zero sized file would would be cached as zero. 1U<<31 is used\n+ * as the substitute because it is the furthest away from 0 and 4GiB.\n+ */\n+static inline unsigned int munge_st_size(off_t st_size) {\n+\tunsigned int sd_size = (unsigned int) st_size;\n+\n+\tif (!sd_size && st_size)\n+\t\treturn 1U<<31;\n+\telse\n+\t\treturn sd_size;\n+}\n+\n void fill_stat_data(struct stat_data *sd, struct stat *st)\n {\n \tsd->sd_ctime.sec = (unsigned int)st->st_ctime;\n@@ -173,7 +189,7 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)\n \tsd->sd_ino = st->st_ino;\n \tsd->sd_uid = st->st_uid;\n \tsd->sd_gid = st->st_gid;\n-\tsd->sd_size = st->st_size;\n+\tsd->sd_size = munge_st_size(st->st_size);\n }\n \n int match_stat_data(const struct stat_data *sd, struct stat *st)\n@@ -212,7 +228,7 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)\n \t\t\tchanged |= INODE_CHANGED;\n #endif\n \n-\tif (sd->sd_size != (unsigned int) st->st_size)\n+\tif (sd->sd_size != munge_st_size(st->st_size))\n \t\tchanged |= DATA_CHANGED;\n \n \treturn changed;\n-- \n2.36.0.3\n\n"}]}