2024-05-26 19:29:15 -07:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2024-03-27 06:38:38 -07:00
|
|
|
|
using System.IO;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2024-03-27 18:34:08 -07:00
|
|
|
|
using Avalonia.Media.Imaging;
|
2024-02-28 19:29:54 -08:00
|
|
|
|
using Avalonia.Threading;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
namespace SourceGit.ViewModels
|
|
|
|
|
{
|
|
|
|
|
public class DiffContext : ObservableObject
|
|
|
|
|
{
|
|
|
|
|
public string RepositoryPath
|
|
|
|
|
{
|
2024-02-27 05:13:52 -08:00
|
|
|
|
get => _repo;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public Models.Change WorkingCopyChange
|
|
|
|
|
{
|
2024-02-27 05:13:52 -08:00
|
|
|
|
get => _option.WorkingCopyChange;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsUnstaged
|
|
|
|
|
{
|
2024-02-27 05:13:52 -08:00
|
|
|
|
get => _option.IsUnstaged;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 21:27:09 -07:00
|
|
|
|
public string Title
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-13 21:27:09 -07:00
|
|
|
|
get => _title;
|
|
|
|
|
private set => SetProperty(ref _title, value);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 21:27:09 -07:00
|
|
|
|
public string FileModeChange
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-13 21:27:09 -07:00
|
|
|
|
get => _fileModeChange;
|
|
|
|
|
private set => SetProperty(ref _fileModeChange, value);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsLoading
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _isLoading;
|
|
|
|
|
private set => SetProperty(ref _isLoading, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsTextDiff
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _isTextDiff;
|
|
|
|
|
private set => SetProperty(ref _isTextDiff, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public object Content
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _content;
|
|
|
|
|
private set => SetProperty(ref _content, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-26 19:29:15 -07:00
|
|
|
|
public int Unified
|
|
|
|
|
{
|
|
|
|
|
get => _unified;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (SetProperty(ref _unified, value))
|
|
|
|
|
LoadDiffContent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-20 03:27:48 -07:00
|
|
|
|
public DiffContext(string repo, Models.DiffOption option, DiffContext previous = null)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo = repo;
|
|
|
|
|
_option = option;
|
2024-03-20 05:34:24 -07:00
|
|
|
|
|
|
|
|
|
if (previous != null)
|
|
|
|
|
{
|
|
|
|
|
_isTextDiff = previous._isTextDiff;
|
|
|
|
|
_content = previous._content;
|
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
|
|
|
|
|
_title = _option.Path;
|
|
|
|
|
else
|
|
|
|
|
_title = $"{_option.OrgPath} → {_option.Path}";
|
|
|
|
|
|
2024-05-26 19:29:15 -07:00
|
|
|
|
LoadDiffContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void IncrUnified()
|
|
|
|
|
{
|
|
|
|
|
Unified = _unified + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DecrUnified()
|
|
|
|
|
{
|
|
|
|
|
Unified = Math.Max(4, _unified - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OpenExternalMergeTool()
|
|
|
|
|
{
|
|
|
|
|
var type = Preference.Instance.ExternalMergeToolType;
|
|
|
|
|
var exec = Preference.Instance.ExternalMergeToolPath;
|
|
|
|
|
|
|
|
|
|
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
|
|
|
|
if (tool == null || !File.Exists(exec))
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo, "Invalid merge tool in preference setting!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var args = tool.Type != 0 ? tool.DiffCmd : Preference.Instance.ExternalMergeToolDiffCmd;
|
|
|
|
|
Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, exec, args, _option));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void LoadDiffContent()
|
|
|
|
|
{
|
2024-03-17 18:37:06 -07:00
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
2024-05-26 19:29:15 -07:00
|
|
|
|
var latest = new Commands.Diff(_repo, _option, _unified).Result();
|
2024-03-27 06:38:38 -07:00
|
|
|
|
var rs = null as object;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-03-27 06:38:38 -07:00
|
|
|
|
if (latest.TextDiff != null)
|
|
|
|
|
{
|
2024-05-28 21:50:26 -07:00
|
|
|
|
var repo = Preference.FindRepository(_repo);
|
|
|
|
|
if (repo != null && repo.Submodules.Contains(_option.Path))
|
|
|
|
|
{
|
|
|
|
|
var submoduleDiff = new Models.SubmoduleDiff();
|
|
|
|
|
var submoduleRoot = $"{_repo}/{_option.Path}".Replace("\\", "/");
|
|
|
|
|
foreach (var line in latest.TextDiff.Lines)
|
|
|
|
|
{
|
|
|
|
|
if (line.Type == Models.TextDiffLineType.Added)
|
|
|
|
|
{
|
|
|
|
|
var sha = line.Content.Substring("Subproject commit ".Length);
|
|
|
|
|
submoduleDiff.New = new Commands.QuerySingleCommit(submoduleRoot, sha).Result();
|
|
|
|
|
}
|
|
|
|
|
else if (line.Type == Models.TextDiffLineType.Deleted)
|
|
|
|
|
{
|
|
|
|
|
var sha = line.Content.Substring("Subproject commit ".Length);
|
|
|
|
|
submoduleDiff.Old = new Commands.QuerySingleCommit(submoduleRoot, sha).Result();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rs = submoduleDiff;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
latest.TextDiff.File = _option.Path;
|
|
|
|
|
rs = latest.TextDiff;
|
|
|
|
|
}
|
2024-03-27 06:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
else if (latest.IsBinary)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath;
|
2024-03-27 06:38:38 -07:00
|
|
|
|
var ext = Path.GetExtension(oldPath);
|
|
|
|
|
|
|
|
|
|
if (IMG_EXTS.Contains(ext))
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-03-27 06:38:38 -07:00
|
|
|
|
var imgDiff = new Models.ImageDiff();
|
2024-05-26 19:29:15 -07:00
|
|
|
|
if (_option.Revisions.Count == 2)
|
2024-03-27 06:38:38 -07:00
|
|
|
|
{
|
2024-05-26 19:29:15 -07:00
|
|
|
|
imgDiff.Old = BitmapFromRevisionFile(_repo, _option.Revisions[0], oldPath);
|
|
|
|
|
imgDiff.New = BitmapFromRevisionFile(_repo, _option.Revisions[1], oldPath);
|
2024-03-27 06:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-05-26 19:29:15 -07:00
|
|
|
|
var fullPath = Path.Combine(_repo, _option.Path);
|
|
|
|
|
imgDiff.Old = BitmapFromRevisionFile(_repo, "HEAD", oldPath);
|
2024-03-27 18:34:08 -07:00
|
|
|
|
imgDiff.New = File.Exists(fullPath) ? new Bitmap(fullPath) : null;
|
2024-03-27 06:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
rs = imgDiff;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-03-27 06:38:38 -07:00
|
|
|
|
var binaryDiff = new Models.BinaryDiff();
|
2024-05-26 19:29:15 -07:00
|
|
|
|
if (_option.Revisions.Count == 2)
|
2024-03-27 06:38:38 -07:00
|
|
|
|
{
|
2024-05-26 19:29:15 -07:00
|
|
|
|
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, _option.Revisions[0]).Result();
|
|
|
|
|
binaryDiff.NewSize = new Commands.QueryFileSize(_repo, _option.Path, _option.Revisions[1]).Result();
|
2024-03-27 06:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-05-26 19:29:15 -07:00
|
|
|
|
var fullPath = Path.Combine(_repo, _option.Path);
|
|
|
|
|
binaryDiff.OldSize = new Commands.QueryFileSize(_repo, oldPath, "HEAD").Result();
|
2024-03-27 18:34:08 -07:00
|
|
|
|
binaryDiff.NewSize = File.Exists(fullPath) ? new FileInfo(fullPath).Length : 0;
|
2024-03-27 06:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
rs = binaryDiff;
|
2024-03-28 02:46:03 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-27 06:38:38 -07:00
|
|
|
|
else if (latest.IsLFS)
|
|
|
|
|
{
|
|
|
|
|
rs = latest.LFSDiff;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rs = new Models.NoOrEOLChange();
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-20 03:27:48 -07:00
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-06-04 05:19:49 -07:00
|
|
|
|
if (_content is Models.TextDiff old && rs is Models.TextDiff cur && old.File == cur.File)
|
|
|
|
|
cur.SyncScrollOffset = old.SyncScrollOffset;
|
|
|
|
|
|
2024-04-13 21:27:09 -07:00
|
|
|
|
FileModeChange = latest.FileModeChange;
|
2024-03-27 06:38:38 -07:00
|
|
|
|
Content = rs;
|
2024-05-28 21:50:26 -07:00
|
|
|
|
IsTextDiff = rs is Models.TextDiff;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
IsLoading = false;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-07 18:57:41 -07:00
|
|
|
|
private Bitmap BitmapFromRevisionFile(string repo, string revision, string file)
|
|
|
|
|
{
|
|
|
|
|
var stream = Commands.QueryFileContent.Run(repo, revision, file);
|
2024-04-07 19:24:28 -07:00
|
|
|
|
return stream.Length > 0 ? new Bitmap(stream) : null;
|
2024-04-07 18:57:41 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-27 06:38:38 -07:00
|
|
|
|
private static readonly HashSet<string> IMG_EXTS = new HashSet<string>()
|
|
|
|
|
{
|
|
|
|
|
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
private readonly string _repo = string.Empty;
|
|
|
|
|
private readonly Models.DiffOption _option = null;
|
2024-04-13 21:27:09 -07:00
|
|
|
|
private string _title = string.Empty;
|
|
|
|
|
private string _fileModeChange = string.Empty;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private bool _isLoading = true;
|
|
|
|
|
private bool _isTextDiff = false;
|
|
|
|
|
private object _content = null;
|
2024-05-26 19:29:15 -07:00
|
|
|
|
private int _unified = 4;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
2024-03-31 01:54:29 -07:00
|
|
|
|
}
|