diff options
author | Paulo Casaretto <pcasaretto@gmail.com> | 2025-08-29 16:02:54 +0000 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2025-08-29 09:46:07 -0700 |
commit | 00727249ec8404c68391ec58e9c9f0d8a88d5ca0 (patch) | |
tree | 466a3bbb6ff3c13198bf303df28df227bc23e98e /range-diff.h | |
parent | f814da676ae46aac5be0a98b99373a76dee6cedb (diff) |
range-diff: add configurable memory limit for cost matrix
When comparing large commit ranges (e.g., 250,000+ commits), range-diff
attempts to allocate an n×n cost matrix that can exhaust available
memory. For example, with 256,784 commits (n = 513,568), the matrix
would require approximately 256GB of memory (513,568² × 4 bytes),
causing either immediate segmentation faults due to integer overflow or
system hangs.
Add a memory limit check in get_correspondences() before allocating the
cost matrix. This check uses the total size in bytes (n² × sizeof(int))
and compares it against a configurable maximum, preventing both
excessive memory usage and integer overflow issues.
The limit is configurable via a new --max-memory option that accepts
human-readable sizes (e.g., "1G", "500M"). The default is 4GB for 64 bit
systems and 2GB for 32 bit systems. This allows comparing ranges of
approximately 32,000 (16,000) commits - generous for real-world use cases
while preventing impractical operations.
When the limit is exceeded, range-diff now displays a clear error
message showing both the requested memory size and the maximum allowed,
formatted in human-readable units for better user experience.
Example usage:
git range-diff --max-memory=1G branch1...branch2
git range-diff --max-memory=500M base..topic1 base..topic2
This approach was chosen over alternatives:
- Pre-counting commits: Would require spawning additional git processes
and reading all commits twice
- Limiting by commit count: Less precise than actual memory usage
- Streaming approach: Would require significant refactoring of the
current algorithm
This issue was previously discussed in:
https://lore.kernel.org/git/RFC-cover-v2-0.5-00000000000-20211210T122901Z-avarab@gmail.com/
Acked-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Paulo Casaretto <pcasaretto@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'range-diff.h')
-rw-r--r-- | range-diff.h | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/range-diff.h b/range-diff.h index cd85000b5a..9d39818e34 100644 --- a/range-diff.h +++ b/range-diff.h @@ -5,6 +5,10 @@ #include "strvec.h" #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60 +#define RANGE_DIFF_MAX_MEMORY_DEFAULT \ + (sizeof(void*) >= 8 ? \ + ((size_t)(1024L * 1024L) * (size_t)(4L * 1024L)) : /* 4GB on 64-bit */ \ + ((size_t)(1024L * 1024L) * (size_t)(2L * 1024L))) /* 2GB on 32-bit */ /* * A much higher value than the default, when we KNOW we are comparing @@ -17,6 +21,7 @@ struct range_diff_options { unsigned dual_color:1; unsigned left_only:1, right_only:1; unsigned include_merges:1; + size_t max_memory; const struct diff_options *diffopt; /* may be NULL */ const struct strvec *other_arg; /* may be NULL */ }; |