Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Models/DiffOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ public DiffOption(string baseRevision, string targetRevision, Change change)
_orgPath = change.OriginalPath;
}

/// <summary>
/// Used to show differences between two files.
/// </summary>
/// <param name="path_lhs"></param>
/// <param name="path_rhs"></param>
public DiffOption(string path_lhs, string path_rhs)
{
_extra = "--no-index";
_orgPath = path_lhs;
_path = path_rhs;
}

/// <summary>
/// Converts to diff command arguments.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Ignore All Whitespace Changes</x:String>
<x:String x:Key="Text.Diff.Last" xml:space="preserve">Last Difference</x:String>
<x:String x:Key="Text.Diff.LFS" xml:space="preserve">LFS OBJECT CHANGE</x:String>
<x:String x:Key="Text.Diff.LFSContent" xml:space="preserve">Show LFS Content</x:String>
<x:String x:Key="Text.Diff.Next" xml:space="preserve">Next Difference</x:String>
<x:String x:Key="Text.Diff.NoChange" xml:space="preserve">NO CHANGES OR ONLY EOL CHANGES</x:String>
<x:String x:Key="Text.Diff.Prev" xml:space="preserve">Previous Difference</x:String>
Expand Down
207 changes: 129 additions & 78 deletions src/ViewModels/DiffContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Avalonia.Threading;

using CommunityToolkit.Mvvm.ComponentModel;
using SourceGit.Models;

namespace SourceGit.ViewModels
{
Expand All @@ -31,6 +32,20 @@ public bool IgnoreWhitespace
}
}

public bool ShowContentInLFSDiff
{
get => Preferences.Instance.ShowContentInLFSDiff;
set
{
if (value != Preferences.Instance.ShowContentInLFSDiff)
{
Preferences.Instance.ShowContentInLFSDiff = value;
OnPropertyChanged();
LoadDiffContent();
}
}
}

public string FileModeChange
{
get => _fileModeChange;
Expand All @@ -42,6 +57,11 @@ public bool IsTextDiff
get => _isTextDiff;
private set => SetProperty(ref _isTextDiff, value);
}
public bool IsLFSDiff
{
get => _isLFSDiff;
private set => SetProperty(ref _isLFSDiff, value);
}

public object Content
{
Expand Down Expand Up @@ -118,112 +138,139 @@ private void LoadDiffContent()
// so instead we set a very high number for the "lines of context" parameter.
var numLines = Preferences.Instance.UseFullTextDiff ? 999999999 : _unifiedLines;
var ignoreWS = Preferences.Instance.IgnoreWhitespaceChangesInDiff;
var showContentInLFSDiff = Preferences.Instance.ShowContentInLFSDiff;
var latest = new Commands.Diff(_repo, _option, numLines, ignoreWS).Result();
var info = new Info(_option, numLines, ignoreWS, latest);
var info = new Info(_option, numLines, ignoreWS, showContentInLFSDiff, latest);
if (_info != null && info.IsSame(_info))
return;

_info = info;

var rs = null as object;
if (latest.TextDiff != null)
var rs = GetDiffObject(latest, _option);
if (latest.IsLFS && showContentInLFSDiff)
{
var newLFSFilePath = GetLFSObjectPath(latest.LFSDiff.New.Oid);
var oldLFSFilePath = latest.LFSDiff.Old.Oid.Length > 0 ? GetLFSObjectPath(latest.LFSDiff.Old.Oid) : "/dev/null";

var oidDiffOption = new DiffOption(oldLFSFilePath, newLFSFilePath);
var oidDiff = new Commands.Diff(_repo, oidDiffOption, numLines, ignoreWS).Result();

// FIXME: if we have an lfs file that points to another lfs file, the showContentInLFSDiff will toggle between the
// original lfs pointer sha and the 1 depth recursed lfs sha pointer
rs = GetDiffObject(oidDiff, oidDiffOption, _option.Path);
}

Dispatcher.UIThread.Post(() =>
{
var count = latest.TextDiff.Lines.Count;
var isSubmodule = false;
if (count <= 3)
if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File)
cur.ScrollOffset = old.ScrollOffset;

FileModeChange = latest.FileModeChange;
Content = rs;
IsLFSDiff = latest.IsLFS;
IsTextDiff = rs is Models.TextDiff;
});
});
}

private object GetDiffObject(DiffResult diffResult, DiffOption diffOption, string LFSOrigFilePath = "")
{
var rs = null as object;
if (diffResult.TextDiff != null)
{
var count = diffResult.TextDiff.Lines.Count;
var isSubmodule = false;
if (count <= 3)
{
var submoduleDiff = new Models.SubmoduleDiff();
var submoduleRoot = $"{_repo}/{diffOption.Path}".Replace("\\", "/");
isSubmodule = true;
for (int i = 1; i < count; i++)
{
var submoduleDiff = new Models.SubmoduleDiff();
var submoduleRoot = $"{_repo}/{_option.Path}".Replace("\\", "/");
isSubmodule = true;
for (int i = 1; i < count; i++)
var line = diffResult.TextDiff.Lines[i];
if (!line.Content.StartsWith("Subproject commit ", StringComparison.Ordinal))
{
var line = latest.TextDiff.Lines[i];
if (!line.Content.StartsWith("Subproject commit ", StringComparison.Ordinal))
{
isSubmodule = false;
break;
}

var sha = line.Content.Substring(18);
if (line.Type == Models.TextDiffLineType.Added)
submoduleDiff.New = QuerySubmoduleRevision(submoduleRoot, sha);
else if (line.Type == Models.TextDiffLineType.Deleted)
submoduleDiff.Old = QuerySubmoduleRevision(submoduleRoot, sha);
isSubmodule = false;
break;
}

if (isSubmodule)
rs = submoduleDiff;
var sha = line.Content.Substring(18);
if (line.Type == Models.TextDiffLineType.Added)
submoduleDiff.New = QuerySubmoduleRevision(submoduleRoot, sha);
else if (line.Type == Models.TextDiffLineType.Deleted)
submoduleDiff.Old = QuerySubmoduleRevision(submoduleRoot, sha);
}

if (!isSubmodule)
{
latest.TextDiff.File = _option.Path;
rs = latest.TextDiff;
}
if (isSubmodule)
rs = submoduleDiff;
}
else if (latest.IsBinary)

if (!isSubmodule)
{
var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath;
var ext = Path.GetExtension(_option.Path);
diffResult.TextDiff.File = diffOption.Path;
rs = diffResult.TextDiff;
}
}
else if (diffResult.IsBinary)
{
var oldPath = string.IsNullOrEmpty(diffOption.OrgPath) ? diffOption.Path : diffOption.OrgPath;
var ext = LFSOrigFilePath.Length == 0 ? Path.GetExtension(diffOption.Path) : Path.GetExtension(LFSOrigFilePath);

if (IMG_EXTS.Contains(ext))
if (IMG_EXTS.Contains(ext))
{
var imgDiff = new Models.ImageDiff();
if (diffOption.Revisions.Count == 2)
{
var imgDiff = new Models.ImageDiff();
if (_option.Revisions.Count == 2)
{
(imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, _option.Revisions[0], oldPath);
(imgDiff.New, imgDiff.NewFileSize) = BitmapFromRevisionFile(_repo, _option.Revisions[1], _option.Path);
}
else
{
if (!oldPath.Equals("/dev/null", StringComparison.Ordinal))
(imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, "HEAD", oldPath);

var fullPath = Path.Combine(_repo, _option.Path);
if (File.Exists(fullPath))
{
imgDiff.New = new Bitmap(fullPath);
imgDiff.NewFileSize = new FileInfo(fullPath).Length;
}
}
rs = imgDiff;
(imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, diffOption.Revisions[0], oldPath);
(imgDiff.New, imgDiff.NewFileSize) = BitmapFromRevisionFile(_repo, diffOption.Revisions[1], diffOption.Path);
}
else
{
var binaryDiff = new Models.BinaryDiff();
if (_option.Revisions.Count == 2)
{
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, _option.Revisions[0]).Result();
binaryDiff.NewSize = new Commands.QueryFileSize(_repo, _option.Path, _option.Revisions[1]).Result();
}
else
if (!oldPath.Equals("/dev/null", StringComparison.Ordinal))
(imgDiff.Old, imgDiff.OldFileSize) = BitmapFromRevisionFile(_repo, "HEAD", oldPath);

var fullPath = Path.Combine(_repo, diffOption.Path);
if (File.Exists(fullPath))
{
var fullPath = Path.Combine(_repo, _option.Path);
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, "HEAD").Result();
binaryDiff.NewSize = File.Exists(fullPath) ? new FileInfo(fullPath).Length : 0;
imgDiff.New = new Bitmap(fullPath);
imgDiff.NewFileSize = new FileInfo(fullPath).Length;
}
rs = binaryDiff;
}
}
else if (latest.IsLFS)
{
rs = latest.LFSDiff;
rs = imgDiff;
}
else
{
rs = new Models.NoOrEOLChange();
var binaryDiff = new Models.BinaryDiff();
if (diffOption.Revisions.Count == 2)
{
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, diffOption.Revisions[0]).Result();
binaryDiff.NewSize = new Commands.QueryFileSize(_repo, diffOption.Path, diffOption.Revisions[1]).Result();
}
else
{
var fullPath = Path.Combine(_repo, diffOption.Path);
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, "HEAD").Result();
binaryDiff.NewSize = File.Exists(fullPath) ? new FileInfo(fullPath).Length : 0;
}
rs = binaryDiff;
}
}
else if (diffResult.IsLFS)
{
rs = diffResult.LFSDiff;
}
else
{
rs = new Models.NoOrEOLChange();
}
return rs;
}

Dispatcher.UIThread.Post(() =>
{
if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File)
cur.ScrollOffset = old.ScrollOffset;
private string GetLFSObjectPath(string oid)
{
var lfsDir = Path.Combine(".git", "lfs", "objects", oid.Substring(0, 2), oid.Substring(2, 2), oid);

FileModeChange = latest.FileModeChange;
Content = rs;
IsTextDiff = rs is Models.TextDiff;
});
});
return lfsDir;
}

private (Bitmap, long) BitmapFromRevisionFile(string repo, string revision, string file)
Expand Down Expand Up @@ -263,14 +310,16 @@ private class Info
public string Argument { get; set; }
public int UnifiedLines { get; set; }
public bool IgnoreWhitespace { get; set; }
public bool ShowContentInLFSDiff { get; set; }
public string OldHash { get; set; }
public string NewHash { get; set; }

public Info(Models.DiffOption option, int unifiedLines, bool ignoreWhitespace, Models.DiffResult result)
public Info(Models.DiffOption option, int unifiedLines, bool ignoreWhitespace, bool showContentInFLSDiff, Models.DiffResult result)
{
Argument = option.ToString();
UnifiedLines = unifiedLines;
IgnoreWhitespace = ignoreWhitespace;
ShowContentInLFSDiff = showContentInFLSDiff;
OldHash = result.OldHash;
NewHash = result.NewHash;
}
Expand All @@ -280,6 +329,7 @@ public bool IsSame(Info other)
return Argument.Equals(other.Argument, StringComparison.Ordinal) &&
UnifiedLines == other.UnifiedLines &&
IgnoreWhitespace == other.IgnoreWhitespace &&
ShowContentInLFSDiff == other.ShowContentInLFSDiff &&
OldHash.Equals(other.OldHash, StringComparison.Ordinal) &&
NewHash.Equals(other.NewHash, StringComparison.Ordinal);
}
Expand All @@ -291,6 +341,7 @@ public bool IsSame(Info other)
private string _fileModeChange = string.Empty;
private int _unifiedLines = 4;
private bool _isTextDiff = false;
private bool _isLFSDiff = false;
private object _content = null;
private Info _info = null;
}
Expand Down
7 changes: 7 additions & 0 deletions src/ViewModels/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,12 @@ public bool IgnoreCRAtEOLInDiff
}
}

public bool ShowContentInLFSDiff
{
get => _showContentInLFSDiff;
set => SetProperty(ref _showContentInLFSDiff, value);
}

public bool IgnoreWhitespaceChangesInDiff
{
get => _ignoreWhitespaceChangesInDiff;
Expand Down Expand Up @@ -687,6 +693,7 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
private bool _showHiddenSymbolsInDiffView = false;
private bool _useFullTextDiff = false;
private bool _useBlockNavigationInDiffView = false;
private bool _showContentInLFSDiff = false;

private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List;
private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List;
Expand Down
8 changes: 8 additions & 0 deletions src/Views/DiffView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@
<Path Width="12" Height="12" Data="{StaticResource Icons.WordWrap}" Margin="0,2,0,0"/>
</ToggleButton>

<ToggleButton Classes="line_path"
Width="28"
IsChecked="{Binding ShowContentInLFSDiff, Mode=TwoWay}"
IsVisible="{Binding IsLFSDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.LFSContent}">
<Path Width="14" Height="14" Stretch="Uniform" Data="{StaticResource Icons.LFS}"/>
</ToggleButton>

<ToggleButton Classes="line_path"
Width="28"
IsChecked="{Binding IgnoreWhitespace, Mode=TwoWay}"
Expand Down