-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix resetting selected lines for renamed files in index #10252
Changes from all commits
be2d92f
0a9f850
e571947
663d435
96a780a
02f2318
2aa3d7a
8219a9a
98d762b
9313a33
c1a9d49
928ce1f
36eb407
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,9 +28,9 @@ public static class PatchManager | |
} | ||
} | ||
|
||
public static byte[]? GetSelectedLinesAsPatch(string text, int selectionPosition, int selectionLength, bool isIndex, Encoding fileContentEncoding, bool isNewFile) | ||
public static byte[]? GetSelectedLinesAsPatch(string text, int selectionPosition, int selectionLength, bool isIndex, Encoding fileContentEncoding, bool reset, bool isNewFile, bool isRenamed) | ||
{ | ||
var selectedChunks = GetSelectedChunks(text, selectionPosition, selectionLength, out var header); | ||
IReadOnlyList<Chunk>? selectedChunks = GetSelectedChunks(text, selectionPosition, selectionLength, out string header); | ||
|
||
if (selectedChunks is null || header is null) | ||
{ | ||
|
@@ -43,9 +43,15 @@ public static class PatchManager | |
header = CorrectHeaderForNewFile(header); | ||
} | ||
|
||
// if file is renamed and selected lines are being reset from index then the patch undoes the rename too | ||
if (isIndex && isRenamed && reset) | ||
{ | ||
header = CorrectHeaderForRenamedFile(header); | ||
} | ||
|
||
string? body = ToIndexPatch(selectedChunks, isIndex, isWholeFile: false); | ||
|
||
if (header is null || body is null) | ||
if (body is null) | ||
{ | ||
return null; | ||
} | ||
|
@@ -82,6 +88,72 @@ private static string CorrectHeaderForNewFile(string header) | |
return sb.ToString(); | ||
} | ||
|
||
private static string CorrectHeaderForRenamedFile(string header) | ||
{ | ||
// Expected input: | ||
// | ||
// diff --git a/original.txt b/original2.txt | ||
// similarity index 88% | ||
// rename from original.txt | ||
// rename to original2.txt | ||
// index 0e05069..d4029ea 100644 | ||
// --- a/original.txt | ||
// +++ b/original2.txt | ||
|
||
// Expected output: | ||
// | ||
// diff --git a/original2.txt b/original2.txt | ||
// index 0e05069..d4029ea 100644 | ||
// --- a/original2.txt | ||
// +++ b/original2.txt | ||
|
||
string[] headerLines = header.Split(new[] { '\n' }); | ||
string? oldNameWithPrefix = null; | ||
string? newName = null; | ||
foreach (string line in headerLines) | ||
{ | ||
if (line.StartsWith("+++")) | ||
{ | ||
// Takes the "original2.txt" part from patch file line: +++ b/original2.txt | ||
newName = line[6..]; | ||
} | ||
else if (line.StartsWith("---")) | ||
{ | ||
// Takes the "a/original.txt" part from patch file line: --- a/original.txt | ||
oldNameWithPrefix = line[4..]; | ||
} | ||
} | ||
|
||
StringBuilder sb = new(); | ||
|
||
for (int i = 0; i < headerLines.Length; i++) | ||
{ | ||
string line = headerLines[i]; | ||
if (line.StartsWith("--- ")) | ||
{ | ||
line = $"--- a/{newName}"; | ||
} | ||
else if (line.Contains($" {oldNameWithPrefix} ")) | ||
{ | ||
line = line.Replace($" {oldNameWithPrefix} ", $" a/{newName} "); | ||
} | ||
else if (line.StartsWith("rename from ") || line.StartsWith("rename to ") || line.StartsWith("similarity index ")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are any of these strings ever translated in Git? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since these are part of a diff format used by git and are called extended headers I think it's safe to assume that that wouldn't happen. At least documentation doesn't mention anything of that sort. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is described as a format, it is not even a 'requested' format, certainly not a porcelain interface. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to push back if I may. Translation argument sounds almost like saying that translating browsers would mean translating HTTP Headers they send (custom ones, not covered by the standard). |
||
{ | ||
// Note: this logic depends on git not localizing patch file format | ||
continue; | ||
} | ||
|
||
if (i != 0) | ||
{ | ||
sb.Append('\n'); | ||
} | ||
|
||
sb.Append(line); | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
|
||
public static byte[]? GetSelectedLinesAsNewPatch(GitModule module, string newFileName, string text, int selectionPosition, int selectionLength, Encoding fileContentEncoding, bool reset, byte[] filePreamble, string? treeGuid) | ||
{ | ||
var selectedChunks = FromNewFile(module, text, selectionPosition, selectionLength, reset, filePreamble, fileContentEncoding); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,6 +73,7 @@ private enum ViewMode | |
private Func<Task>? _deferShowFunc; | ||
private readonly ContinuousScrollEventManager _continuousScrollEventManager; | ||
private FileStatusItem? _viewItem; | ||
private readonly TaskDialogPage _NO_TRANSLATE_resetSelectedLinesConfirmationDialog; | ||
|
||
private static string[] _rangeDiffFullPrefixes = { " ", " ++", " + ", " +", " --", " - ", " -", " +-", " -+", " " }; | ||
private static string[] _combinedDiffFullPrefixes = { " ", "++", "+ ", " +", "--", "- ", " -" }; | ||
|
@@ -197,6 +198,16 @@ public FileViewer() | |
|
||
_fullPathResolver = new FullPathResolver(() => Module.WorkingDir); | ||
SupportLinePatching = false; | ||
|
||
_NO_TRANSLATE_resetSelectedLinesConfirmationDialog = new() | ||
{ | ||
Text = TranslatedStrings.ResetSelectedLinesConfirmation, | ||
Caption = TranslatedStrings.ResetChangesCaption, | ||
Icon = TaskDialogIcon.Warning, | ||
Buttons = { TaskDialogButton.Yes, TaskDialogButton.No }, | ||
DefaultButton = TaskDialogButton.Yes, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a good idea? I think the default button for destructive operations should be the one that doesn't lead to data loss. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
SizeToContent = true, | ||
}; | ||
} | ||
|
||
// Public properties | ||
|
@@ -1446,7 +1457,9 @@ public void StageSelectedLines(bool stage) | |
GetSelectionLength(), | ||
isIndex: !stage, | ||
Encoding, | ||
_viewItem.Item.IsNew); | ||
reset: false, | ||
_viewItem.Item.IsNew, | ||
_viewItem.Item.IsRenamed); | ||
} | ||
|
||
if (patch is null || patch.Length == 0) | ||
|
@@ -1476,8 +1489,7 @@ public void ResetNoncommittedSelectedLines() | |
return; | ||
} | ||
|
||
if (MessageBox.Show(this, TranslatedStrings.ResetSelectedLinesConfirmation, TranslatedStrings.ResetChangesCaption, | ||
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) | ||
if (TaskDialog.ShowDialog(Handle, _NO_TRANSLATE_resetSelectedLinesConfirmationDialog) == TaskDialogButton.No) | ||
{ | ||
return; | ||
} | ||
|
@@ -1508,7 +1520,9 @@ public void ResetNoncommittedSelectedLines() | |
GetSelectionLength(), | ||
isIndex: true, | ||
Encoding, | ||
_viewItem.Item.IsNew); | ||
reset: true, | ||
_viewItem.Item.IsNew, | ||
_viewItem.Item.IsRenamed); | ||
} | ||
else | ||
{ | ||
|
@@ -1579,7 +1593,9 @@ private void ApplySelectedLines(bool allFile, bool reverse) | |
selectionLength, | ||
isIndex: false, | ||
Encoding, | ||
_viewItem.Item.IsNew); | ||
reset: false, | ||
_viewItem.Item.IsNew, | ||
_viewItem.Item.IsRenamed); | ||
} | ||
else | ||
{ | ||
|
@@ -1983,6 +1999,7 @@ public bool ShowSyntaxHighlightingInDiff | |
set => _fileViewer.ShowSyntaxHighlightingInDiff = value; | ||
} | ||
|
||
public TaskDialogPage ResetSelectedLinesConfirmationDialog => _fileViewer._NO_TRANSLATE_resetSelectedLinesConfirmationDialog; | ||
public ToolStripButton IgnoreWhitespaceAtEolButton => _fileViewer.ignoreWhitespaceAtEol; | ||
public ToolStripMenuItem IgnoreWhitespaceAtEolMenuItem => _fileViewer.ignoreWhitespaceAtEolToolStripMenuItem; | ||
|
||
|
@@ -1992,6 +2009,8 @@ public bool ShowSyntaxHighlightingInDiff | |
public ToolStripButton IgnoreAllWhitespacesButton => _fileViewer.ignoreAllWhitespaces; | ||
public ToolStripMenuItem IgnoreAllWhitespacesMenuItem => _fileViewer.ignoreAllWhitespaceChangesToolStripMenuItem; | ||
|
||
internal CommandStatus ExecuteCommand(Command command) => _fileViewer.ExecuteCommand((int)command); | ||
|
||
internal void IgnoreWhitespaceAtEolToolStripMenuItem_Click(object sender, EventArgs e) => _fileViewer.IgnoreWhitespaceAtEolToolStripMenuItem_Click(sender, e); | ||
internal void IgnoreWhitespaceChangesToolStripMenuItemClick(object sender, EventArgs e) => _fileViewer.IgnoreWhitespaceChangesToolStripMenuItemClick(sender, e); | ||
internal void IgnoreAllWhitespaceChangesToolStripMenuItem_Click(object sender, EventArgs e) => _fileViewer.IgnoreAllWhitespaceChangesToolStripMenuItem_Click(sender, e); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using NUnit.Framework; | ||
|
||
[SetUpFixture] | ||
public class GlobalSetupOnce | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this class get used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the NUnit docs
Since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you |
||
{ | ||
[OneTimeSetUp] | ||
public void RunBeforeAnyTests() | ||
{ | ||
Application.EnableVisualStyles(); | ||
Application.SetCompatibleTextRenderingDefault(false); | ||
} | ||
|
||
[OneTimeTearDown] | ||
public void RunAfterAnyTests() | ||
{ | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a risky check. I believe that a/, b/ is forced right now in GE, but Git could change the output format any time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose that would be a serious breaking change (and not just for GE). Also git documentation mentions these prefixes explicitly https://git-scm.com/docs/diff-format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be changed with diff.mnemonicprefix=true. It was forced to false some time ago so a/b is likely stable.
But number of spaces etc are not porcelain and can be changed without notice.
It could be different in existing Git versions too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you approve of using regex here? Something like:
^---\s+(?<a>.+)/(?<aName>.+)$
and^\+\+\+\s+(?<b>.+)/(?<bName>.+)$
to capture a prefix used and a file name as well as make it more resilient to spacing.