From d7753d7d67c4e4bb0036663148ff957038b3a73b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 30 Jul 2026 14:19:17 +0200 Subject: [PATCH 1/3] mingw: handle staging 4GB+ files correctly This is yet another set of `unsigned long` declarations that should have been `size_t` ones. Signed-off-by: Johannes Schindelin --- git-zlib.c | 2 +- object-file.c | 2 +- object-file.h | 2 +- odb.c | 2 +- odb.h | 4 ++-- odb/source.h | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/git-zlib.c b/git-zlib.c index ebbbcc6d1a5077..1c94f90497ee47 100644 --- a/git-zlib.c +++ b/git-zlib.c @@ -33,7 +33,7 @@ static const char *zerr_to_string(int status) /* uLong is 32-bit on Windows, even on 64-bit systems */ #define ULONG_MAX_VALUE maximum_unsigned_value_of_type(uLong) -static inline uInt zlib_buf_cap(unsigned long len) +static inline uInt zlib_buf_cap(size_t len) { return (ZLIB_BUF_MAX < len) ? ZLIB_BUF_MAX : len; } diff --git a/object-file.c b/object-file.c index 6453b1d6fa7d1b..30c62b41bc73c1 100644 --- a/object-file.c +++ b/object-file.c @@ -749,7 +749,7 @@ static int end_loose_object_common(struct odb_source_loose *loose, int write_loose_object(struct odb_source_loose *loose, const struct object_id *oid, char *hdr, - int hdrlen, const void *buf, unsigned long len, + int hdrlen, const void *buf, size_t len, time_t mtime, unsigned flags) { int fd, ret; diff --git a/object-file.h b/object-file.h index 4c87cd160bb58a..7c1df3bb10c76c 100644 --- a/object-file.h +++ b/object-file.h @@ -139,7 +139,7 @@ void write_object_file_prepare(const struct git_hash_algo *algo, char *hdr, size_t *hdrlen); int write_loose_object(struct odb_source_loose *loose, const struct object_id *oid, char *hdr, - int hdrlen, const void *buf, unsigned long len, + int hdrlen, const void *buf, size_t len, time_t mtime, unsigned flags); /* Helper to check and "touch" a file */ diff --git a/odb.c b/odb.c index 7d555be09feaea..fd9b87e99a245f 100644 --- a/odb.c +++ b/odb.c @@ -986,7 +986,7 @@ void odb_assert_oid_type(struct object_database *odb, } int odb_write_object_ext(struct object_database *odb, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid, diff --git a/odb.h b/odb.h index 3834a0dcbf033b..06af0b847d13d4 100644 --- a/odb.h +++ b/odb.h @@ -548,14 +548,14 @@ enum odb_write_object_flags { * Returns 0 on success, a negative error code otherwise. */ int odb_write_object_ext(struct object_database *odb, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid, enum odb_write_object_flags flags); static inline int odb_write_object(struct object_database *odb, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid) { diff --git a/odb/source.h b/odb/source.h index 1c65a05e2c4c6b..3ba75ec4ee1e85 100644 --- a/odb/source.h +++ b/odb/source.h @@ -407,7 +407,7 @@ static inline int odb_source_freshen_object(struct odb_source *source, * object ID and the compatibility object ID, if non-NULL. */ static inline int odb_source_write_object(struct odb_source *source, - const void *buf, unsigned long len, + const void *buf, size_t len, enum object_type type, struct object_id *oid, struct object_id *compat_oid, From a60a705f4256e76a9feb11c02cfdc2650c90744a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 30 Jul 2026 14:42:25 +0200 Subject: [PATCH 2/3] mingw: support unpacking loose 4GB+ objects ... and yet another round of `unsigned long` -> `size_t` transmogrifications. Signed-off-by: Johannes Schindelin --- object-file.c | 10 +++++----- object-file.h | 6 +++--- odb/source-loose.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/object-file.c b/object-file.c index 30c62b41bc73c1..002c08b970ccee 100644 --- a/object-file.c +++ b/object-file.c @@ -146,7 +146,7 @@ int stream_object_signature(struct repository *r, * Map and close the given loose object fd. The path argument is used for * error reporting. */ -static void *map_fd(int fd, const char *path, unsigned long *size) +static void *map_fd(int fd, const char *path, size_t *size) { void *map = NULL; struct stat st; @@ -167,9 +167,9 @@ static void *map_fd(int fd, const char *path, unsigned long *size) enum unpack_loose_header_result unpack_loose_header(git_zstream *stream, unsigned char *map, - unsigned long mapsize, + size_t mapsize, void *buffer, - unsigned long bufsiz) + size_t bufsiz) { int status; @@ -200,7 +200,7 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream, } void *unpack_loose_rest(git_zstream *stream, - void *buffer, unsigned long size, + void *buffer, size_t size, const struct object_id *oid) { size_t bytes = strlen(buffer) + 1, n; @@ -1613,7 +1613,7 @@ int read_loose_object(struct repository *repo, int ret = -1; int fd; void *map = NULL; - unsigned long mapsize; + size_t mapsize; git_zstream stream; char hdr[MAX_HEADER_LEN]; size_t *size = oi->sizep; diff --git a/object-file.h b/object-file.h index 7c1df3bb10c76c..1470469d28ba2c 100644 --- a/object-file.h +++ b/object-file.h @@ -180,11 +180,11 @@ enum unpack_loose_header_result { */ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream, unsigned char *map, - unsigned long mapsize, + size_t mapsize, void *buffer, - unsigned long bufsiz); + size_t bufsiz); void *unpack_loose_rest(git_zstream *stream, - void *buffer, unsigned long size, + void *buffer, size_t size, const struct object_id *oid); int parse_loose_header(const char *hdr, struct object_info *oi); diff --git a/odb/source-loose.c b/odb/source-loose.c index 6211348a4d3561..51bcb0b2e05845 100644 --- a/odb/source-loose.c +++ b/odb/source-loose.c @@ -68,7 +68,7 @@ static int read_object_info_from_path(struct odb_source_loose *loose, { int ret; int fd; - unsigned long mapsize; + size_t mapsize; void *map = NULL; git_zstream stream, *stream_to_end = NULL; char hdr[MAX_HEADER_LEN]; @@ -248,7 +248,7 @@ static int open_loose_object(struct odb_source_loose *loose, static void *odb_source_loose_map_object(struct odb_source_loose *loose, const struct object_id *oid, - unsigned long *size) + size_t *size) { const char *p; int fd = open_loose_object(loose, oid, &p); @@ -283,7 +283,7 @@ struct odb_loose_read_stream { ODB_LOOSE_READ_STREAM_ERROR, } z_state; void *mapped; - unsigned long mapsize; + size_t mapsize; char hdr[32]; int hdr_avail; int hdr_used; @@ -354,7 +354,7 @@ static int odb_source_loose_read_object_stream(struct odb_read_stream **out, struct odb_source_loose *loose = odb_source_loose_downcast(source); struct object_info oi = OBJECT_INFO_INIT; struct odb_loose_read_stream *st; - unsigned long mapsize; + size_t mapsize; void *mapped; mapped = odb_source_loose_map_object(loose, oid, &mapsize); From 5d1c2d2ef2b4077e5d715a9b9bf2ad0e4c49656d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 30 Jul 2026 14:57:54 +0200 Subject: [PATCH 3/3] t: add an expensive test to verify that 4GB+ blobs can be staged/read I should have added this test as soon as https://github.com/git-for-windows/git/issues/6012 was opened. Signed-off-by: Johannes Schindelin --- t/meson.build | 1 + t/t3706-add-4gb-file.sh | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100755 t/t3706-add-4gb-file.sh diff --git a/t/meson.build b/t/meson.build index 85cb439bdda599..c86bf0de910cda 100644 --- a/t/meson.build +++ b/t/meson.build @@ -427,6 +427,7 @@ integration_tests = [ 't3703-add-magic-pathspec.sh', 't3704-add-pathspec-file.sh', 't3705-add-sparse-checkout.sh', + 't3706-add-4gb-file.sh', 't3800-mktag.sh', 't3900-i18n-commit.sh', 't3901-i18n-patch.sh', diff --git a/t/t3706-add-4gb-file.sh b/t/t3706-add-4gb-file.sh new file mode 100755 index 00000000000000..5978ca3ee24c8a --- /dev/null +++ b/t/t3706-add-4gb-file.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Copyright (c) 2026 Johannes Schindelin +# + +test_description='add 4GB+ objects' + +. ./test-lib.sh + +if ! test_have_prereq EXPENSIVE,SIZE_T_IS_64BIT +then + skip_all='expensive 4GB blob test; enable on 64-bit with GIT_TEST_LONG=true' + test_done +fi + +size_4gb=4294967296 + +test_expect_success 'set up a 4GB file' ' + test_atexit "rm -f large" && + # genrandom takes only an unsigned long... + test-tool genrandom 123 $(($size_4gb-1)) >large && + printf 1 >>large +' + +test_expect_success 'add 4GB file' ' + git add large && + git cat-file -s :large >size-staged && + test $size_4gb = $(cat size-staged) +' +test_expect_success 'read 4GB loose object' ' + git -P show :large >read && + test_file_size read >size-read && + test $size_4gb = $(cat size-read) +' + +test_done