enhance: try load lfs image from local cache first before loading it by git lfs smudge command

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-08-26 21:17:45 +08:00
parent 764265160b
commit 2df1830ffc
8 changed files with 74 additions and 30 deletions

View File

@@ -0,0 +1,27 @@
using System.IO;
using System.Threading.Tasks;
namespace SourceGit.Commands
{
public class QueryGitCommonDir : Command
{
public QueryGitCommonDir(string workDir)
{
WorkingDirectory = workDir;
Args = "rev-parse --git-common-dir";
RaiseError = false;
}
public async Task<string> GetResultAsync()
{
var rs = await ReadToEndAsync().ConfigureAwait(false);
if (!rs.IsSuccess || string.IsNullOrEmpty(rs.StdOut))
return string.Empty;
var dir = rs.StdOut.Trim();
if (Path.IsPathRooted(dir))
return dir;
return Path.GetFullPath(Path.Combine(WorkingDirectory, dir));
}
}
}

View File

@@ -36,7 +36,7 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _viewContent, value);
}
public FileHistoriesSingleRevision(Repository repo, string file, Models.Commit revision, bool prevIsDiffMode)
public FileHistoriesSingleRevision(string repo, string file, Models.Commit revision, bool prevIsDiffMode)
{
_repo = repo;
_file = file;
@@ -49,7 +49,7 @@ namespace SourceGit.ViewModels
public async Task<bool> ResetToSelectedRevisionAsync()
{
return await new Commands.Checkout(_repo.FullPath)
return await new Commands.Checkout(_repo)
.FileWithRevisionAsync(_file, $"{_revision.SHA}")
.ConfigureAwait(false);
}
@@ -59,13 +59,13 @@ namespace SourceGit.ViewModels
if (_viewContent is not FileHistoriesRevisionFile { CanOpenWithDefaultEditor: true })
return;
var fullPath = Native.OS.GetAbsPath(_repo.FullPath, _file);
var fullPath = Native.OS.GetAbsPath(_repo, _file);
var fileName = Path.GetFileNameWithoutExtension(fullPath) ?? "";
var fileExt = Path.GetExtension(fullPath) ?? "";
var tmpFile = Path.Combine(Path.GetTempPath(), $"{fileName}~{_revision.SHA.AsSpan(0, 10)}{fileExt}");
await Commands.SaveRevisionFile
.RunAsync(_repo.FullPath, _revision.SHA, _file, tmpFile)
.RunAsync(_repo, _revision.SHA, _file, tmpFile)
.ConfigureAwait(false);
Native.OS.OpenWithDefaultEditor(tmpFile);
@@ -81,7 +81,7 @@ namespace SourceGit.ViewModels
Task.Run(async () =>
{
var objs = await new Commands.QueryRevisionObjects(_repo.FullPath, _revision.SHA, _file)
var objs = await new Commands.QueryRevisionObjects(_repo, _revision.SHA, _file)
.GetResultAsync()
.ConfigureAwait(false);
@@ -100,23 +100,23 @@ namespace SourceGit.ViewModels
{
if (obj.Type == Models.ObjectType.Blob)
{
var isBinary = await new Commands.IsBinary(_repo.FullPath, _revision.SHA, _file).GetResultAsync().ConfigureAwait(false);
var isBinary = await new Commands.IsBinary(_repo, _revision.SHA, _file).GetResultAsync().ConfigureAwait(false);
if (isBinary)
{
var imgDecoder = ImageSource.GetDecoder(_file);
if (imgDecoder != Models.ImageDecoder.None)
{
var source = await ImageSource.FromRevisionAsync(_repo.FullPath, _revision.SHA, _file, imgDecoder).ConfigureAwait(false);
var source = await ImageSource.FromRevisionAsync(_repo, _revision.SHA, _file, imgDecoder).ConfigureAwait(false);
var image = new Models.RevisionImageFile(_file, source.Bitmap, source.Size);
return new FileHistoriesRevisionFile(_file, image, true);
}
var size = await new Commands.QueryFileSize(_repo.FullPath, _file, _revision.SHA).GetResultAsync().ConfigureAwait(false);
var size = await new Commands.QueryFileSize(_repo, _file, _revision.SHA).GetResultAsync().ConfigureAwait(false);
var binaryFile = new Models.RevisionBinaryFile() { Size = size };
return new FileHistoriesRevisionFile(_file, binaryFile, true);
}
var contentStream = await Commands.QueryFileContent.RunAsync(_repo.FullPath, _revision.SHA, _file).ConfigureAwait(false);
var contentStream = await Commands.QueryFileContent.RunAsync(_repo, _revision.SHA, _file).ConfigureAwait(false);
var content = await new StreamReader(contentStream).ReadToEndAsync();
var lfs = Models.LFSObject.Parse(content);
if (lfs != null)
@@ -124,7 +124,7 @@ namespace SourceGit.ViewModels
var imgDecoder = ImageSource.GetDecoder(_file);
if (imgDecoder != Models.ImageDecoder.None)
{
var combined = new RevisionLFSImage(_repo.FullPath, _file, lfs, imgDecoder);
var combined = new RevisionLFSImage(_repo, _file, lfs, imgDecoder);
return new FileHistoriesRevisionFile(_file, combined, true);
}
@@ -138,7 +138,7 @@ namespace SourceGit.ViewModels
if (obj.Type == Models.ObjectType.Commit)
{
var submoduleRoot = Path.Combine(_repo.FullPath, _file);
var submoduleRoot = Path.Combine(_repo, _file);
var commit = await new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false);
var message = commit != null ? await new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).GetResultAsync().ConfigureAwait(false) : null;
var module = new Models.RevisionSubmodule()
@@ -156,10 +156,10 @@ namespace SourceGit.ViewModels
private void SetViewContentAsDiff()
{
var option = new Models.DiffOption(_revision, _file);
ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext);
ViewContent = new DiffContext(_repo, option, _viewContent as DiffContext);
}
private Repository _repo = null;
private string _repo = null;
private string _file = null;
private Models.Commit _revision = null;
private bool _isDiffMode = false;
@@ -186,7 +186,7 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _viewContent, value);
}
public FileHistoriesCompareRevisions(Repository repo, string file, Models.Commit start, Models.Commit end)
public FileHistoriesCompareRevisions(string repo, string file, Models.Commit start, Models.Commit end)
{
_repo = repo;
_file = file;
@@ -204,7 +204,7 @@ namespace SourceGit.ViewModels
public async Task<bool> SaveAsPatch(string saveTo)
{
return await Commands.SaveChangesAsPatch
.ProcessRevisionCompareChangesAsync(_repo.FullPath, _changes, _startPoint.SHA, _endPoint.SHA, saveTo)
.ProcessRevisionCompareChangesAsync(_repo, _changes, _startPoint.SHA, _endPoint.SHA, saveTo)
.ConfigureAwait(false);
}
@@ -212,7 +212,7 @@ namespace SourceGit.ViewModels
{
Task.Run(async () =>
{
_changes = await new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA, _file).ReadAsync().ConfigureAwait(false);
_changes = await new Commands.CompareRevisions(_repo, _startPoint.SHA, _endPoint.SHA, _file).ReadAsync().ConfigureAwait(false);
if (_changes.Count == 0)
{
Dispatcher.UIThread.Post(() => ViewContent = null);
@@ -220,12 +220,12 @@ namespace SourceGit.ViewModels
else
{
var option = new Models.DiffOption(_startPoint.SHA, _endPoint.SHA, _changes[0]);
Dispatcher.UIThread.Post(() => ViewContent = new DiffContext(_repo.FullPath, option, _viewContent));
Dispatcher.UIThread.Post(() => ViewContent = new DiffContext(_repo, option, _viewContent));
}
});
}
private Repository _repo = null;
private string _repo = null;
private string _file = null;
private Models.Commit _startPoint = null;
private Models.Commit _endPoint = null;
@@ -264,7 +264,7 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _viewContent, value);
}
public FileHistories(Repository repo, string file, string commit = null)
public FileHistories(string repo, string file, string commit = null)
{
if (!string.IsNullOrEmpty(commit))
Title = $"{file} @ {commit}";
@@ -282,7 +282,7 @@ namespace SourceGit.ViewModels
.Append(" -- ")
.Append(file.Quoted());
var commits = await new Commands.QueryCommits(_repo.FullPath, argsBuilder.ToString(), false)
var commits = await new Commands.QueryCommits(_repo, argsBuilder.ToString(), false)
.GetResultAsync()
.ConfigureAwait(false);
@@ -311,7 +311,18 @@ namespace SourceGit.ViewModels
public void NavigateToCommit(Models.Commit commit)
{
_repo.NavigateToCommit(commit.SHA);
var launcher = App.GetLauncher();
if (launcher != null)
{
foreach (var page in launcher.Pages)
{
if (page.Data is Repository repo && repo.FullPath.Equals(_repo, StringComparison.Ordinal))
{
repo.NavigateToCommit(commit.SHA);
break;
}
}
}
}
public string GetCommitFullMessage(Models.Commit commit)
@@ -320,12 +331,12 @@ namespace SourceGit.ViewModels
if (_fullCommitMessages.TryGetValue(sha, out var msg))
return msg;
msg = new Commands.QueryCommitFullMessage(_repo.FullPath, sha).GetResult();
msg = new Commands.QueryCommitFullMessage(_repo, sha).GetResult();
_fullCommitMessages[sha] = msg;
return msg;
}
private readonly Repository _repo = null;
private readonly string _repo = null;
private bool _isLoading = true;
private bool _prevIsDiffMode = true;
private List<Models.Commit> _commits = null;

View File

@@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
@@ -52,7 +53,12 @@ namespace SourceGit.ViewModels
if (string.IsNullOrEmpty(lfs.Oid) || lfs.Size == 0)
return new ImageSource(null, 0);
var stream = await Commands.QueryFileContent.FromLFSAsync(repo, lfs.Oid, lfs.Size).ConfigureAwait(false);
var commonDir = await new Commands.QueryGitCommonDir(repo).GetResultAsync().ConfigureAwait(false);
var localFile = Path.Combine(commonDir, "lfs", "objects", lfs.Oid.Substring(0, 2), lfs.Oid.Substring(2, 2), lfs.Oid);
if (File.Exists(localFile))
return await FromFileAsync(localFile, decoder).ConfigureAwait(false);
await using var stream = await Commands.QueryFileContent.FromLFSAsync(repo, lfs.Oid, lfs.Size).ConfigureAwait(false);
return await Task.Run(() => LoadFromStream(stream, decoder)).ConfigureAwait(false);
}

View File

@@ -25,7 +25,7 @@ namespace SourceGit.ViewModels
{
var source = await ImageSource.FromLFSObjectAsync(repo, lfs, decoder).ConfigureAwait(false);
var img = new Models.RevisionImageFile(file, source.Bitmap, source.Size);
Dispatcher.UIThread.Invoke(() => Image = img);
Dispatcher.UIThread.Post(() => Image = img);
});
}

View File

@@ -137,7 +137,7 @@ namespace SourceGit.Views
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo, change.Path, commit.SHA));
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path, commit.SHA));
ev.Handled = true;
};

View File

@@ -535,7 +535,7 @@ namespace SourceGit.Views
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo, file.Path, commit.SHA));
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, file.Path, commit.SHA));
ev.Handled = true;
};

View File

@@ -248,7 +248,7 @@ namespace SourceGit.Views
histories.Icon = App.CreateMenuIcon("Icons.Histories");
histories.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo, submodule.Path));
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, submodule.Path));
ev.Handled = true;
};

View File

@@ -620,7 +620,7 @@ namespace SourceGit.Views
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo, change.Path));
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
e.Handled = true;
};
@@ -1086,7 +1086,7 @@ namespace SourceGit.Views
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new ViewModels.FileHistories(repo, change.Path));
App.ShowWindow(new ViewModels.FileHistories(repo.FullPath, change.Path));
e.Handled = true;
};