mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-11-01 13:13:21 -07:00
feature<TextDiffView>: implementation for stage/unstage/discard selected changes in file
This commit is contained in:
parent
671e46f8b3
commit
9e4d349b51
3 changed files with 447 additions and 207 deletions
|
@ -6,7 +6,6 @@ using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace SourceGit.Models {
|
namespace SourceGit.Models {
|
||||||
public enum TextDiffLineType {
|
public enum TextDiffLineType {
|
||||||
|
@ -34,10 +36,370 @@ namespace SourceGit.Models {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TextDiffSelection {
|
||||||
|
public int StartLine { get; set; } = 0;
|
||||||
|
public int EndLine { get; set; } = 0;
|
||||||
|
public bool HasChanges { get; set; } = false;
|
||||||
|
public bool HasLeftChanges { get; set; } = false;
|
||||||
|
public int IgnoredAdds { get; set; } = 0;
|
||||||
|
public int IgnoredDeletes { get; set; } = 0;
|
||||||
|
|
||||||
|
public bool IsInRange(int idx) {
|
||||||
|
return idx >= StartLine - 1 && idx < EndLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class TextDiff {
|
public class TextDiff {
|
||||||
public string File { get; set; } = string.Empty;
|
public string File { get; set; } = string.Empty;
|
||||||
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
|
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
|
||||||
public int MaxLineNumber = 0;
|
public int MaxLineNumber = 0;
|
||||||
|
|
||||||
|
public void GenerateNewPatchFromSelection(Change change, string fileBlobGuid, TextDiffSelection selection, bool revert, string output) {
|
||||||
|
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
|
||||||
|
var fileGuid = isTracked ? fileBlobGuid.Substring(0, 8) : "00000000";
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
|
||||||
|
if (!revert && !isTracked) builder.Append("new file mode 100644\n");
|
||||||
|
builder.Append("index 00000000...").Append(fileGuid).Append('\n');
|
||||||
|
builder.Append("--- ").Append((revert || isTracked) ? $"a/{change.Path}\n" : "/dev/null\n");
|
||||||
|
builder.Append("+++ b/").Append(change.Path).Append('\n');
|
||||||
|
|
||||||
|
var additions = selection.EndLine - selection.StartLine;
|
||||||
|
if (selection.StartLine != 1) additions++;
|
||||||
|
|
||||||
|
if (revert) {
|
||||||
|
var totalLines = Lines.Count - 1;
|
||||||
|
builder.Append($"@@ -0,").Append(totalLines - additions).Append(" +0,").Append(totalLines).Append(" @@");
|
||||||
|
for (int i = 1; i <= totalLines; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type != TextDiffLineType.Added) continue;
|
||||||
|
builder.Append(selection.IsInRange(i) ? "\n+" : "\n ").Append(line.Content);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.Append("@@ -0,0 +0,").Append(additions).Append(" @@");
|
||||||
|
for (int i = selection.StartLine - 1; i < selection.EndLine; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type != TextDiffLineType.Added) continue;
|
||||||
|
builder.Append("\n+").Append(line.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("\n\\ No newline at end of file\n");
|
||||||
|
System.IO.File.WriteAllText(output, builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GeneratePatchFromSelection(Change change, string fileTreeGuid, TextDiffSelection selection, bool revert, string output) {
|
||||||
|
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
|
||||||
|
builder.Append("index 00000000...").Append(fileTreeGuid).Append(" 100644\n");
|
||||||
|
builder.Append("--- a/").Append(orgFile).Append('\n');
|
||||||
|
builder.Append("+++ b/").Append(change.Path);
|
||||||
|
|
||||||
|
// If last line of selection is a change. Find one more line.
|
||||||
|
var tail = null as string;
|
||||||
|
if (selection.EndLine < Lines.Count) {
|
||||||
|
var lastLine = Lines[selection.EndLine - 1];
|
||||||
|
if (lastLine.Type == TextDiffLineType.Added || lastLine.Type == TextDiffLineType.Deleted) {
|
||||||
|
for (int i = selection.EndLine; i < Lines.Count; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) break;
|
||||||
|
if (revert) {
|
||||||
|
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Added) {
|
||||||
|
tail = line.Content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Deleted) {
|
||||||
|
tail = line.Content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first line is not indicator.
|
||||||
|
if (Lines[selection.StartLine - 1].Type != TextDiffLineType.Indicator) {
|
||||||
|
var indicator = selection.StartLine - 1;
|
||||||
|
for (int i = selection.StartLine - 2; i >= 0; i--) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
indicator = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ignoreAdds = 0;
|
||||||
|
var ignoreRemoves = 0;
|
||||||
|
for (int i = 0; i < indicator; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Added) {
|
||||||
|
ignoreAdds++;
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
ignoreRemoves++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = indicator; i < selection.StartLine - 1; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
ProcessIndicatorForPatch(builder, line, i, selection.StartLine, selection.EndLine, ignoreRemoves, ignoreAdds, revert, tail != null);
|
||||||
|
} else if (line.Type == TextDiffLineType.Added) {
|
||||||
|
if (revert) builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
if (!revert) builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Normal) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the selected lines.
|
||||||
|
for (int i = selection.StartLine - 1; i < selection.EndLine; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
if (!ProcessIndicatorForPatch(builder, line, i, selection.StartLine, selection.EndLine, selection.IgnoredDeletes, selection.IgnoredAdds, revert, tail != null)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (line.Type == TextDiffLineType.Normal) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Added) {
|
||||||
|
builder.Append("\n+").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
builder.Append("\n-").Append(line.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("\n ").Append(tail);
|
||||||
|
builder.Append("\n");
|
||||||
|
System.IO.File.WriteAllText(output, builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GeneratePatchFromSelectionSingleSide(Change change, string fileTreeGuid, TextDiffSelection selection, bool revert, bool isOldSide, string output) {
|
||||||
|
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
|
||||||
|
builder.Append("index 00000000...").Append(fileTreeGuid).Append(" 100644\n");
|
||||||
|
builder.Append("--- a/").Append(orgFile).Append('\n');
|
||||||
|
builder.Append("+++ b/").Append(change.Path);
|
||||||
|
|
||||||
|
// If last line of selection is a change. Find one more line.
|
||||||
|
var tail = null as string;
|
||||||
|
if (selection.EndLine < Lines.Count) {
|
||||||
|
var lastLine = Lines[selection.EndLine - 1];
|
||||||
|
if (lastLine.Type == TextDiffLineType.Added || lastLine.Type == TextDiffLineType.Deleted) {
|
||||||
|
for (int i = selection.EndLine; i < Lines.Count; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) break;
|
||||||
|
if (revert) {
|
||||||
|
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Added) {
|
||||||
|
tail = line.Content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.Type == TextDiffLineType.Normal || line.Type == TextDiffLineType.Deleted) {
|
||||||
|
tail = line.Content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first line is not indicator.
|
||||||
|
if (Lines[selection.StartLine - 1].Type != TextDiffLineType.Indicator) {
|
||||||
|
var indicator = selection.StartLine - 1;
|
||||||
|
for (int i = selection.StartLine - 2; i >= 0; i--) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
indicator = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ignoreAdds = 0;
|
||||||
|
var ignoreRemoves = 0;
|
||||||
|
for (int i = 0; i < indicator; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Added) {
|
||||||
|
ignoreAdds++;
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
ignoreRemoves++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = indicator; i < selection.StartLine - 1; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
ProcessIndicatorForPatchSingleSide(builder, line, i, selection.StartLine, selection.EndLine, ignoreRemoves, ignoreAdds, revert, isOldSide, tail != null);
|
||||||
|
} else if (line.Type == TextDiffLineType.Added) {
|
||||||
|
if (revert) builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
if (!revert) builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Normal) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the selected lines.
|
||||||
|
for (int i = selection.StartLine - 1; i < selection.EndLine; i++) {
|
||||||
|
var line = Lines[i];
|
||||||
|
if (line.Type == TextDiffLineType.Indicator) {
|
||||||
|
if (!ProcessIndicatorForPatchSingleSide(builder, line, i, selection.StartLine, selection.EndLine, selection.IgnoredDeletes, selection.IgnoredAdds, revert, isOldSide, tail != null)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (line.Type == TextDiffLineType.Normal) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
} else if (line.Type == TextDiffLineType.Added) {
|
||||||
|
if (isOldSide) {
|
||||||
|
if (revert) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
} else {
|
||||||
|
selection.IgnoredAdds++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.Append("\n+").Append(line.Content);
|
||||||
|
}
|
||||||
|
} else if (line.Type == TextDiffLineType.Deleted) {
|
||||||
|
if (isOldSide) {
|
||||||
|
builder.Append("\n-").Append(line.Content);
|
||||||
|
} else {
|
||||||
|
if (!revert) {
|
||||||
|
builder.Append("\n ").Append(line.Content);
|
||||||
|
} else {
|
||||||
|
selection.IgnoredDeletes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("\n ").Append(tail);
|
||||||
|
builder.Append("\n");
|
||||||
|
System.IO.File.WriteAllText(output, builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ProcessIndicatorForPatch(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool tailed) {
|
||||||
|
var indicatorRegex = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@");
|
||||||
|
|
||||||
|
var match = indicatorRegex.Match(indicator.Content);
|
||||||
|
var oldStart = int.Parse(match.Groups[1].Value);
|
||||||
|
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
|
||||||
|
var oldCount = 0;
|
||||||
|
var newCount = 0;
|
||||||
|
for (int i = idx + 1; i < end; i++) {
|
||||||
|
var test = Lines[i];
|
||||||
|
if (test.Type == TextDiffLineType.Indicator) break;
|
||||||
|
|
||||||
|
if (test.Type == TextDiffLineType.Normal) {
|
||||||
|
oldCount++;
|
||||||
|
newCount++;
|
||||||
|
} else if (test.Type == TextDiffLineType.Added) {
|
||||||
|
if (i < start - 1) {
|
||||||
|
if (revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == end - 1 && tailed) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else if (test.Type == TextDiffLineType.Deleted) {
|
||||||
|
if (i < start - 1) {
|
||||||
|
if (!revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == end - 1 && tailed) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldCount == 0 && newCount == 0) return false;
|
||||||
|
|
||||||
|
builder.Append($"\n@@ -{oldStart},{oldCount} +{newStart},{newCount} @@");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ProcessIndicatorForPatchSingleSide(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool isOldSide, bool tailed) {
|
||||||
|
var indicatorRegex = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@");
|
||||||
|
|
||||||
|
var match = indicatorRegex.Match(indicator.Content);
|
||||||
|
var oldStart = int.Parse(match.Groups[1].Value);
|
||||||
|
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
|
||||||
|
var oldCount = 0;
|
||||||
|
var newCount = 0;
|
||||||
|
for (int i = idx + 1; i < end; i++) {
|
||||||
|
var test = Lines[i];
|
||||||
|
if (test.Type == TextDiffLineType.Indicator) break;
|
||||||
|
|
||||||
|
if (test.Type == TextDiffLineType.Normal) {
|
||||||
|
oldCount++;
|
||||||
|
newCount++;
|
||||||
|
} else if (test.Type == TextDiffLineType.Added) {
|
||||||
|
if (i < start - 1) {
|
||||||
|
if (revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isOldSide) {
|
||||||
|
if (revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == end - 1 && tailed) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else if (test.Type == TextDiffLineType.Deleted) {
|
||||||
|
if (i < start - 1) {
|
||||||
|
if (!revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isOldSide) {
|
||||||
|
oldCount++;
|
||||||
|
} else {
|
||||||
|
if (!revert) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == end - 1 && tailed) {
|
||||||
|
newCount++;
|
||||||
|
oldCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldCount == 0 && newCount == 0) return false;
|
||||||
|
|
||||||
|
builder.Append($"\n@@ -{oldStart},{oldCount} +{newStart},{newCount} @@");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LFSDiff {
|
public class LFSDiff {
|
||||||
|
|
|
@ -16,23 +16,9 @@ using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using TextMateSharp.Grammars;
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
namespace SourceGit.Views {
|
namespace SourceGit.Views {
|
||||||
public class TextDiffUnifiedSelection {
|
|
||||||
public int StartLine { get; set; } = 0;
|
|
||||||
public int EndLine { get; set; } = 0;
|
|
||||||
public bool HasChanges { get; set; } = false;
|
|
||||||
public bool HasLeftChanges { get; set; } = false;
|
|
||||||
public int IgnoredAdds { get; set; } = 0;
|
|
||||||
public int IgnoredDeletes { get; set; } = 0;
|
|
||||||
|
|
||||||
public bool IsInRange(int idx) {
|
|
||||||
return idx >= StartLine - 1 && idx < EndLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CombinedTextDiffPresenter : TextEditor {
|
public class CombinedTextDiffPresenter : TextEditor {
|
||||||
public class LineNumberMargin : AbstractMargin {
|
public class LineNumberMargin : AbstractMargin {
|
||||||
public LineNumberMargin(CombinedTextDiffPresenter editor, bool isOldLine) {
|
public LineNumberMargin(CombinedTextDiffPresenter editor, bool isOldLine) {
|
||||||
|
@ -706,10 +692,13 @@ namespace SourceGit.Views {
|
||||||
|
|
||||||
var tmpFile = Path.GetTempFileName();
|
var tmpFile = Path.GetTempFileName();
|
||||||
if (change.WorkTree == Models.ChangeState.Untracked) {
|
if (change.WorkTree == Models.ChangeState.Untracked) {
|
||||||
GenerateNewPatchFromSelection(change, null, selection, false, tmpFile);
|
TextDiff.GenerateNewPatchFromSelection(change, null, selection, false, tmpFile);
|
||||||
} else if (UseCombined) {
|
} else if (UseCombined) {
|
||||||
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
||||||
GenerateCombinedPatchFromSelection(change, treeGuid, selection, false, tmpFile);
|
TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, false, tmpFile);
|
||||||
|
} else {
|
||||||
|
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
||||||
|
TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, false, isOldSide, tmpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index").Exec();
|
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index").Exec();
|
||||||
|
@ -729,10 +718,13 @@ namespace SourceGit.Views {
|
||||||
|
|
||||||
var tmpFile = Path.GetTempFileName();
|
var tmpFile = Path.GetTempFileName();
|
||||||
if (change.WorkTree == Models.ChangeState.Untracked) {
|
if (change.WorkTree == Models.ChangeState.Untracked) {
|
||||||
GenerateNewPatchFromSelection(change, null, selection, true, tmpFile);
|
TextDiff.GenerateNewPatchFromSelection(change, null, selection, true, tmpFile);
|
||||||
} else if (UseCombined) {
|
} else if (UseCombined) {
|
||||||
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
||||||
GenerateCombinedPatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
||||||
|
} else {
|
||||||
|
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
||||||
|
TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--reverse").Exec();
|
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--reverse").Exec();
|
||||||
|
@ -756,9 +748,11 @@ namespace SourceGit.Views {
|
||||||
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
var treeGuid = new Commands.QueryStagedFileBlobGuid(ctx.RepositoryPath, change.Path).Result();
|
||||||
var tmpFile = Path.GetTempFileName();
|
var tmpFile = Path.GetTempFileName();
|
||||||
if (change.Index == Models.ChangeState.Added) {
|
if (change.Index == Models.ChangeState.Added) {
|
||||||
GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
TextDiff.GenerateNewPatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
||||||
} else if (UseCombined) {
|
} else if (UseCombined) {
|
||||||
GenerateCombinedPatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
TextDiff.GeneratePatchFromSelection(change, treeGuid, selection, true, tmpFile);
|
||||||
|
} else {
|
||||||
|
TextDiff.GeneratePatchFromSelectionSingleSide(change, treeGuid, selection, true, isOldSide, tmpFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index --reverse").Exec();
|
new Commands.Apply(ctx.RepositoryPath, tmpFile, true, "nowarn", "--cache --index --reverse").Exec();
|
||||||
|
@ -789,203 +783,88 @@ namespace SourceGit.Views {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextDiffUnifiedSelection GetUnifiedSelection(int startLine, int endLine, bool isOldSide) {
|
private Models.TextDiffSelection GetUnifiedSelection(int startLine, int endLine, bool isOldSide) {
|
||||||
var rs = new TextDiffUnifiedSelection();
|
var rs = new Models.TextDiffSelection();
|
||||||
if (Content is Models.TextDiff combined) {
|
var diff = TextDiff;
|
||||||
rs.StartLine = startLine;
|
|
||||||
rs.EndLine = endLine;
|
|
||||||
|
|
||||||
for (int i = 0; i < startLine - 1; i++) {
|
|
||||||
var line = combined.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Added) {
|
|
||||||
rs.HasLeftChanges = true;
|
|
||||||
rs.IgnoredAdds++;
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
rs.HasLeftChanges = true;
|
|
||||||
rs.IgnoredDeletes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (Content is ViewModels.TwoSideTextDiff twoSides) {
|
||||||
|
var target = isOldSide ? twoSides.Old : twoSides.New;
|
||||||
|
var firstContentLine = -1;
|
||||||
for (int i = startLine - 1; i < endLine; i++) {
|
for (int i = startLine - 1; i < endLine; i++) {
|
||||||
var line = combined.Lines[i];
|
var line = target[i];
|
||||||
if (line.Type == Models.TextDiffLineType.Added || line.Type == Models.TextDiffLineType.Deleted) {
|
if (line.Type != Models.TextDiffLineType.None) {
|
||||||
rs.HasChanges = true;
|
firstContentLine = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rs.HasLeftChanges) {
|
if (firstContentLine < 0) return rs;
|
||||||
for (int i = endLine; i < combined.Lines.Count; i++) {
|
|
||||||
var line = combined.Lines[i];
|
var endContentLine = -1;
|
||||||
if (line.Type == Models.TextDiffLineType.Added || line.Type == Models.TextDiffLineType.Deleted) {
|
for (int i = Math.Min(endLine - 1, target.Count - 1); i >= startLine - 1; i--) {
|
||||||
rs.HasLeftChanges = true;
|
var line = target[i];
|
||||||
break;
|
if (line.Type != Models.TextDiffLineType.None) {
|
||||||
}
|
endContentLine = i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Content is ViewModels.TwoSideTextDiff twoSides) {
|
|
||||||
|
|
||||||
|
if (endContentLine < 0) return rs;
|
||||||
|
|
||||||
|
var firstContent = target[firstContentLine];
|
||||||
|
var endContent = target[endContentLine];
|
||||||
|
startLine = TextDiff.Lines.IndexOf(firstContent) + 1;
|
||||||
|
endLine = TextDiff.Lines.IndexOf(endContent) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.StartLine = startLine;
|
||||||
|
rs.EndLine = endLine;
|
||||||
|
|
||||||
|
for (int i = 0; i < startLine - 1; i++) {
|
||||||
|
var line = diff.Lines[i];
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added) {
|
||||||
|
rs.HasLeftChanges = true;
|
||||||
|
rs.IgnoredAdds++;
|
||||||
|
} else if (line.Type == Models.TextDiffLineType.Deleted) {
|
||||||
|
rs.HasLeftChanges = true;
|
||||||
|
rs.IgnoredDeletes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startLine - 1; i < endLine; i++) {
|
||||||
|
var line = diff.Lines[i];
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added) {
|
||||||
|
if (UseCombined) {
|
||||||
|
rs.HasChanges = true;
|
||||||
|
break;
|
||||||
|
} else if (isOldSide) {
|
||||||
|
rs.HasLeftChanges = true;
|
||||||
|
} else {
|
||||||
|
rs.HasChanges = true;
|
||||||
|
}
|
||||||
|
} else if (line.Type == Models.TextDiffLineType.Deleted) {
|
||||||
|
if (UseCombined) {
|
||||||
|
rs.HasChanges = true;
|
||||||
|
break;
|
||||||
|
} else if (isOldSide) {
|
||||||
|
rs.HasChanges = true;
|
||||||
|
} else {
|
||||||
|
rs.HasLeftChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rs.HasLeftChanges) {
|
||||||
|
for (int i = endLine; i < diff.Lines.Count; i++) {
|
||||||
|
var line = diff.Lines[i];
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added || line.Type == Models.TextDiffLineType.Deleted) {
|
||||||
|
rs.HasLeftChanges = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateNewPatchFromSelection(Models.Change change, string fileBlobGuid, TextDiffUnifiedSelection selection, bool revert, string output) {
|
|
||||||
var isTracked = !string.IsNullOrEmpty(fileBlobGuid);
|
|
||||||
var fileGuid = isTracked ? fileBlobGuid.Substring(0, 8) : "00000000";
|
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
|
|
||||||
if (!revert && !isTracked) builder.Append("new file mode 100644\n");
|
|
||||||
builder.Append("index 00000000...").Append(fileGuid).Append('\n');
|
|
||||||
builder.Append("--- ").Append((revert || isTracked) ? $"a/{change.Path}\n" : "/dev/null\n");
|
|
||||||
builder.Append("+++ b/").Append(change.Path).Append('\n');
|
|
||||||
|
|
||||||
var additions = selection.EndLine - selection.StartLine;
|
|
||||||
if (selection.StartLine != 1) additions++;
|
|
||||||
|
|
||||||
if (revert) {
|
|
||||||
var totalLines = TextDiff.Lines.Count - 1;
|
|
||||||
builder.Append($"@@ -0,").Append(totalLines - additions).Append(" +0,").Append(totalLines).Append(" @@");
|
|
||||||
for (int i = 1; i <= totalLines; i++) {
|
|
||||||
var line = TextDiff.Lines[i];
|
|
||||||
if (line.Type != Models.TextDiffLineType.Added) continue;
|
|
||||||
builder.Append(selection.IsInRange(i) ? "\n+" : "\n ").Append(line.Content);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.Append("@@ -0,0 +0,").Append(additions).Append(" @@");
|
|
||||||
for (int i = selection.StartLine - 1; i < selection.EndLine; i++) {
|
|
||||||
var line = TextDiff.Lines[i];
|
|
||||||
if (line.Type != Models.TextDiffLineType.Added) continue;
|
|
||||||
builder.Append("\n+").Append(line.Content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.Append("\n\\ No newline at end of file\n");
|
|
||||||
File.WriteAllText(output, builder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateCombinedPatchFromSelection(Models.Change change, string fileTreeGuid, TextDiffUnifiedSelection selection, bool revert, string output) {
|
|
||||||
var orgFile = !string.IsNullOrEmpty(change.OriginalPath) ? change.OriginalPath : change.Path;
|
|
||||||
var indicatorRegex = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@");
|
|
||||||
var diff = TextDiff;
|
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.Append("diff --git a/").Append(change.Path).Append(" b/").Append(change.Path).Append('\n');
|
|
||||||
builder.Append("index 00000000...").Append(fileTreeGuid).Append(" 100644\n");
|
|
||||||
builder.Append("--- a/").Append(orgFile).Append('\n');
|
|
||||||
builder.Append("+++ b/").Append(change.Path).Append('\n');
|
|
||||||
|
|
||||||
// If last line of selection is a change. Find one more line.
|
|
||||||
var tail = null as string;
|
|
||||||
if (selection.EndLine < diff.Lines.Count) {
|
|
||||||
var lastLine = diff.Lines[selection.EndLine - 1];
|
|
||||||
if (lastLine.Type == Models.TextDiffLineType.Added || lastLine.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
for (int i = selection.EndLine; i < diff.Lines.Count; i++) {
|
|
||||||
var line = diff.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Indicator) break;
|
|
||||||
if (line.Type == Models.TextDiffLineType.Normal || line.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
tail = line.Content;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the first line is not indicator.
|
|
||||||
if (diff.Lines[selection.StartLine - 1].Type != Models.TextDiffLineType.Indicator) {
|
|
||||||
var indicator = selection.StartLine - 1;
|
|
||||||
for (int i = selection.StartLine - 2; i >= 0; i--) {
|
|
||||||
var line = diff.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Indicator) {
|
|
||||||
indicator = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ignoreAdds = 0;
|
|
||||||
var ignoreRemoves = 0;
|
|
||||||
for (int i = 0; i < indicator; i++) {
|
|
||||||
var line = diff.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Added) {
|
|
||||||
ignoreAdds++;
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
ignoreRemoves++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = indicator; i < selection.StartLine - 1; i++) {
|
|
||||||
var line = diff.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Indicator) {
|
|
||||||
ProcessIndicatorForPatch(builder, line, i, selection.StartLine, selection.EndLine, ignoreRemoves, ignoreAdds, tail != null);
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Added) {
|
|
||||||
// Ignores
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Deleted || line.Type == Models.TextDiffLineType.Normal) {
|
|
||||||
// Traits ignored deleted as normal.
|
|
||||||
builder.Append("\n ").Append(line.Content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outputs the selected lines.
|
|
||||||
for (int i = selection.StartLine - 1; i < selection.EndLine; i++) {
|
|
||||||
var line = diff.Lines[i];
|
|
||||||
if (line.Type == Models.TextDiffLineType.Indicator) {
|
|
||||||
if (!ProcessIndicatorForPatch(builder, line, i, selection.StartLine, selection.EndLine, selection.IgnoredDeletes, selection.IgnoredAdds, tail != null)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Normal) {
|
|
||||||
builder.Append("\n ").Append(line.Content);
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Added) {
|
|
||||||
builder.Append("\n+").Append(line.Content);
|
|
||||||
} else if (line.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
builder.Append("\n-").Append(line.Content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.Append("\n ").Append(tail);
|
|
||||||
builder.Append("\n");
|
|
||||||
File.WriteAllText(output, builder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ProcessIndicatorForPatch(StringBuilder builder, Models.TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool tailed) {
|
|
||||||
var indicatorRegex = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@");
|
|
||||||
var diff = TextDiff;
|
|
||||||
|
|
||||||
var match = indicatorRegex.Match(indicator.Content);
|
|
||||||
var oldStart = int.Parse(match.Groups[1].Value);
|
|
||||||
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
|
|
||||||
var oldCount = 0;
|
|
||||||
var newCount = 0;
|
|
||||||
for (int i = idx + 1; i < end; i++) {
|
|
||||||
var test = diff.Lines[i];
|
|
||||||
if (test.Type == Models.TextDiffLineType.Indicator) break;
|
|
||||||
|
|
||||||
if (test.Type == Models.TextDiffLineType.Normal) {
|
|
||||||
oldCount++;
|
|
||||||
newCount++;
|
|
||||||
} else if (test.Type == Models.TextDiffLineType.Added) {
|
|
||||||
if (i >= start - 1) newCount++;
|
|
||||||
|
|
||||||
if (i == end - 1 && tailed) {
|
|
||||||
newCount++;
|
|
||||||
oldCount++;
|
|
||||||
}
|
|
||||||
} else if (test.Type == Models.TextDiffLineType.Deleted) {
|
|
||||||
if (i < start - 1) newCount++;
|
|
||||||
oldCount++;
|
|
||||||
|
|
||||||
if (i == end - 1 && tailed) {
|
|
||||||
newCount++;
|
|
||||||
oldCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldCount == 0 && newCount == 0) return false;
|
|
||||||
|
|
||||||
builder.Append($"@@ -{oldStart},{oldCount} +{newStart},{newCount} @@");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue