diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs
index fb008aeee..728ee13ff 100644
--- a/src/Commands/Diff.cs
+++ b/src/Commands/Diff.cs
@@ -38,6 +38,10 @@ public partial class Diff : Command
private const int MAX_INLINE_CONTENT_LENGTH = 1024;
private const int MAX_INLINE_CHUNKS_PER_LINE = 16;
+ public const long MAX_TEXT_DIFF_SIZE = 5 * 1024 * 1024;
+
+ public bool IgnoreSizeLimit { get; set; } = false;
+
public Diff(string repo, Models.DiffOption opt, int numContextLines, bool ignoreWhitespace, bool ignoreCRAtEOL)
{
_result.TextDiff = new Models.TextDiff();
@@ -66,7 +70,22 @@ public Diff(string repo, Models.DiffOption opt, int numContextLines, bool ignore
proc.Start();
using var ms = new MemoryStream();
- await proc.StandardOutput.BaseStream.CopyToAsync(ms, CancellationToken).ConfigureAwait(false);
+ var stdout = proc.StandardOutput.BaseStream;
+ var chunk = new byte[81920];
+ int read;
+ while ((read = await stdout.ReadAsync(chunk, CancellationToken).ConfigureAwait(false)) > 0)
+ {
+ ms.Write(chunk, 0, read);
+
+ // Stop reading huge diffs (generated files, minified bundles, data dumps, etc.). Only
+ // the diff headers will be parsed.
+ if (!IgnoreSizeLimit && ms.Length > MAX_TEXT_DIFF_SIZE)
+ {
+ _result.IsTooLarge = true;
+ proc.Kill(true);
+ break;
+ }
+ }
if (ms.TryGetBuffer(out var buffer))
{
@@ -82,7 +101,7 @@ public Diff(string repo, Models.DiffOption opt, int numContextLines, bool ignore
}
ParseLine(buffer[start..lineEnd]);
- if (_result.IsBinary)
+ if (_result.IsBinary || (_result.IsTooLarge && _isInChunk))
break;
start = lineEnd + 1;
@@ -96,7 +115,7 @@ public Diff(string repo, Models.DiffOption opt, int numContextLines, bool ignore
// Ignore exceptions.
}
- if (_isLFS || _result.IsBinary || _result.TextDiff.Lines.Count == 0)
+ if (_isLFS || _result.IsBinary || _result.IsTooLarge || _result.TextDiff.Lines.Count == 0)
{
_result.TextDiff = null;
}
diff --git a/src/Models/DiffResult.cs b/src/Models/DiffResult.cs
index 1eba6a02d..33a0b4c79 100644
--- a/src/Models/DiffResult.cs
+++ b/src/Models/DiffResult.cs
@@ -92,6 +92,11 @@ public class EmptyFile
public class NoOrEOLChange;
+ public class TooLargeDiff
+ {
+ public long MaxSizeInMB { get; set; } = 0;
+ }
+
public class SubmoduleDiff
{
public string FullPath { get; set; } = string.Empty;
@@ -107,6 +112,7 @@ public class DiffResult
{
public bool IsBinary { get; set; } = false;
public bool IsSubmoduleChange { get; set; } = false;
+ public bool IsTooLarge { get; set; } = false;
public string OldHash { get; set; } = string.Empty;
public string NewHash { get; set; } = string.Empty;
public int OldMode { get; set; } = 0;
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 603d223a9..d97d8551e 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -398,6 +398,9 @@
Swap
Syntax Highlighting
Line Word Wrap
+ DIFF TOO LARGE TO DISPLAY
+ SHOW ANYWAY
+ The diff output exceeds {0} MB. Loading it may make SourceGit unresponsive for a while.
Open in Merge Tool
Show All Lines
Decrease Number of Visible Lines
diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs
index f268e5ceb..d10b4cf9a 100644
--- a/src/ViewModels/DiffContext.cs
+++ b/src/ViewModels/DiffContext.cs
@@ -63,6 +63,7 @@ public DiffContext(string repo, Models.DiffOption option, DiffContext previous =
_newMode = previous._newMode;
_unifiedLines = previous._unifiedLines;
_info = previous._info;
+ _ignoreSizeLimit = previous._ignoreSizeLimit && previous._option.ToString().Equals(_option.ToString(), StringComparison.Ordinal);
}
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
@@ -85,6 +86,13 @@ public void DecrUnified()
LoadContent();
}
+ public void ShowTooLargeDiff()
+ {
+ _ignoreSizeLimit = true;
+ _info = null;
+ LoadContent();
+ }
+
public void OpenExternalMergeTool()
{
new Commands.DiffTool(_repo, _option).Open();
@@ -134,7 +142,7 @@ private void LoadContent()
var ignoreWhitespace = pref.IgnoreWhitespaceChangesInDiff;
var ignoreCRAtEOL = pref.IgnoreCRAtEOLInDiff;
- var latest = await new Commands.Diff(_repo, _option, numLines, ignoreWhitespace, ignoreCRAtEOL)
+ var latest = await new Commands.Diff(_repo, _option, numLines, ignoreWhitespace, ignoreCRAtEOL) { IgnoreSizeLimit = _ignoreSizeLimit }
.ReadAsync()
.ConfigureAwait(false);
@@ -149,6 +157,10 @@ private void LoadContent()
{
rs = textDiff;
}
+ else if (latest.IsTooLarge)
+ {
+ rs = new Models.TooLargeDiff() { MaxSizeInMB = Commands.Diff.MAX_TEXT_DIFF_SIZE / 1024 / 1024 };
+ }
else if (latest.LFSDiff is { } lfs)
{
var imgDecoder = ImageSource.GetDecoder(_option.Path);
@@ -388,6 +400,7 @@ public bool IsSame(Info other)
private int _unifiedLines = 4;
private bool _isTextDiff = false;
private bool _isIgnoreWhitespaceVisible = true;
+ private bool _ignoreSizeLimit = false;
private object _content = null;
private Info _info = null;
}
diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml
index ba889d09b..9fdab41fe 100644
--- a/src/Views/DiffView.axaml
+++ b/src/Views/DiffView.axaml
@@ -411,6 +411,27 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Views/DiffView.axaml.cs b/src/Views/DiffView.axaml.cs
index 22c16aca1..296f293c2 100644
--- a/src/Views/DiffView.axaml.cs
+++ b/src/Views/DiffView.axaml.cs
@@ -99,5 +99,13 @@ private async void OnOpenBinaryFileViewer(object sender, RoutedEventArgs e)
e.Handled = true;
}
}
+
+ private void OnShowTooLargeDiff(object _, RoutedEventArgs e)
+ {
+ if (DataContext is ViewModels.DiffContext vm)
+ vm.ShowTooLargeDiff();
+
+ e.Handled = true;
+ }
}
}