mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-20 21:01:06 +08:00
feature: allow partial stage/unstage/discard for non-UTF8 text in diff view (#2260)
Current implementation fails on partial stage/unstage/discard operations for non-UTF8 text because of applying a patch made with broken (replaced) text. This modification allows these operation by preserving the original raw bytes from the output of `git diff`, and use it to create patch file.
This commit is contained in:
@@ -45,24 +45,23 @@ namespace SourceGit.Commands
|
||||
using var proc = new Process();
|
||||
proc.StartInfo = CreateGitStartInfo(true);
|
||||
proc.Start();
|
||||
|
||||
var text = await proc.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
|
||||
|
||||
using var ms = new System.IO.MemoryStream();
|
||||
await proc.StandardOutput.BaseStream.CopyToAsync(ms, CancellationToken).ConfigureAwait(false);
|
||||
var bytes = ms.ToArray();
|
||||
var start = 0;
|
||||
var end = text.IndexOf('\n', start);
|
||||
while (end > 0)
|
||||
while (start < bytes.Length)
|
||||
{
|
||||
var line = text[start..end];
|
||||
ParseLine(line);
|
||||
|
||||
start = end + 1;
|
||||
end = text.IndexOf('\n', start);
|
||||
var end = Array.IndexOf(bytes, (byte)'\n', start);
|
||||
if (end < 0)
|
||||
end = bytes.Length;
|
||||
var next = end + 1;
|
||||
if (start <= end - 1 && bytes[end - 1] == '\r')
|
||||
end--;
|
||||
if (!_result.IsBinary)
|
||||
ParseLine(bytes[start..end]);
|
||||
start = next;
|
||||
}
|
||||
|
||||
if (start < text.Length)
|
||||
ParseLine(text[start..]);
|
||||
|
||||
await proc.WaitForExitAsync().ConfigureAwait(false);
|
||||
await proc.WaitForExitAsync(CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -82,10 +81,9 @@ namespace SourceGit.Commands
|
||||
return _result;
|
||||
}
|
||||
|
||||
private void ParseLine(string line)
|
||||
private void ParseLine(byte[] lineBytes)
|
||||
{
|
||||
if (_result.IsBinary)
|
||||
return;
|
||||
var line = Encoding.UTF8.GetString(lineBytes);
|
||||
|
||||
if (line.StartsWith("old mode ", StringComparison.Ordinal))
|
||||
{
|
||||
@@ -168,7 +166,7 @@ namespace SourceGit.Commands
|
||||
|
||||
_oldLine = int.Parse(match.Groups[1].Value);
|
||||
_newLine = int.Parse(match.Groups[2].Value);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, lineBytes, 0, 0);
|
||||
_result.TextDiff.Lines.Add(_last);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +175,7 @@ namespace SourceGit.Commands
|
||||
if (line.Length == 0)
|
||||
{
|
||||
ProcessInlineHighlights();
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, "", _oldLine, _newLine);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, Array.Empty<byte>(), _oldLine, _newLine);
|
||||
_result.TextDiff.Lines.Add(_last);
|
||||
_oldLine++;
|
||||
_newLine++;
|
||||
@@ -195,7 +193,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
|
||||
_result.TextDiff.DeletedLines++;
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, line.Substring(1), _oldLine, 0);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Deleted, lineBytes[1..], _oldLine, 0);
|
||||
_deleted.Add(_last);
|
||||
_oldLine++;
|
||||
}
|
||||
@@ -209,7 +207,7 @@ namespace SourceGit.Commands
|
||||
}
|
||||
|
||||
_result.TextDiff.AddedLines++;
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, line.Substring(1), 0, _newLine);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Added, lineBytes[1..], 0, _newLine);
|
||||
_added.Add(_last);
|
||||
_newLine++;
|
||||
}
|
||||
@@ -221,7 +219,7 @@ namespace SourceGit.Commands
|
||||
{
|
||||
_oldLine = int.Parse(match.Groups[1].Value);
|
||||
_newLine = int.Parse(match.Groups[2].Value);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, line, 0, 0);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Indicator, lineBytes, 0, 0);
|
||||
_result.TextDiff.Lines.Add(_last);
|
||||
}
|
||||
else
|
||||
@@ -233,7 +231,7 @@ namespace SourceGit.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, line.Substring(1), _oldLine, _newLine);
|
||||
_last = new Models.TextDiffLine(Models.TextDiffLineType.Normal, lineBytes[1..], _oldLine, _newLine);
|
||||
_result.TextDiff.Lines.Add(_last);
|
||||
_oldLine++;
|
||||
_newLine++;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace SourceGit.Models
|
||||
public class TextDiffLine
|
||||
{
|
||||
public TextDiffLineType Type { get; set; } = TextDiffLineType.None;
|
||||
public byte[] RawContent { get; set; } = [];
|
||||
public string Content { get; set; } = "";
|
||||
public int OldLineNumber { get; set; } = 0;
|
||||
public int NewLineNumber { get; set; } = 0;
|
||||
@@ -33,10 +34,13 @@ namespace SourceGit.Models
|
||||
public string NewLine => NewLineNumber == 0 ? string.Empty : NewLineNumber.ToString();
|
||||
|
||||
public TextDiffLine() { }
|
||||
public TextDiffLine(TextDiffLineType type, string content, int oldLine, int newLine)
|
||||
public TextDiffLine(TextDiffLineType type, byte[] rawContent, int oldLine, int newLine)
|
||||
{
|
||||
if (rawContent == null)
|
||||
throw new System.ArgumentNullException(nameof(rawContent));
|
||||
Type = type;
|
||||
Content = content;
|
||||
Content = System.Text.Encoding.UTF8.GetString(rawContent);
|
||||
RawContent = rawContent;
|
||||
OldLineNumber = oldLine;
|
||||
NewLineNumber = newLine;
|
||||
}
|
||||
@@ -158,7 +162,7 @@ namespace SourceGit.Models
|
||||
writer.WriteLine($"+++ b/{change.Path}");
|
||||
|
||||
// If last line of selection is a change. Find one more line.
|
||||
string tail = null;
|
||||
TextDiffLine tail = null;
|
||||
if (selection.EndLine < Lines.Count)
|
||||
{
|
||||
var lastLine = Lines[selection.EndLine - 1];
|
||||
@@ -173,7 +177,7 @@ namespace SourceGit.Models
|
||||
(revert && line.Type == TextDiffLineType.Added) ||
|
||||
(!revert && line.Type == TextDiffLineType.Deleted))
|
||||
{
|
||||
tail = line.Content;
|
||||
tail = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -256,8 +260,8 @@ namespace SourceGit.Models
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tail))
|
||||
writer.WriteLine($" {tail}");
|
||||
if (tail != null)
|
||||
WriteLine(writer, ' ', tail);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
@@ -273,7 +277,7 @@ namespace SourceGit.Models
|
||||
writer.WriteLine($"+++ b/{change.Path}");
|
||||
|
||||
// If last line of selection is a change. Find one more line.
|
||||
string tail = null;
|
||||
TextDiffLine tail = null;
|
||||
if (selection.EndLine < Lines.Count)
|
||||
{
|
||||
var lastLine = Lines[selection.EndLine - 1];
|
||||
@@ -288,7 +292,7 @@ namespace SourceGit.Models
|
||||
{
|
||||
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Added)
|
||||
{
|
||||
tail = line.Content;
|
||||
tail = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -296,7 +300,7 @@ namespace SourceGit.Models
|
||||
{
|
||||
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Deleted)
|
||||
{
|
||||
tail = line.Content;
|
||||
tail = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -408,8 +412,8 @@ namespace SourceGit.Models
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tail))
|
||||
writer.WriteLine($" {tail}");
|
||||
if (tail != null)
|
||||
WriteLine(writer, ' ', tail);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
@@ -564,7 +568,10 @@ namespace SourceGit.Models
|
||||
|
||||
private static void WriteLine(StreamWriter writer, char prefix, TextDiffLine line)
|
||||
{
|
||||
writer.WriteLine($"{prefix}{line.Content}");
|
||||
writer.Write($"{prefix}");
|
||||
writer.Flush();
|
||||
writer.BaseStream.Write(line.RawContent); // write original bytes
|
||||
writer.WriteLine();
|
||||
|
||||
if (line.NoNewLineEndOfFile)
|
||||
writer.WriteLine("\\ No newline at end of file");
|
||||
|
||||
Reference in New Issue
Block a user