feature: add Reset File(s) to <revision> context menu entry to selected change(s) in compare view (#2079)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2026-01-29 14:53:18 +08:00
parent 741a732795
commit f545eeacd8
5 changed files with 186 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -14,6 +15,11 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _isLoading, value);
}
public bool CanResetFiles
{
get => _canResetFiles;
}
public string BaseName
{
get => _baseName;
@@ -81,9 +87,10 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _diffContext, value);
}
public Compare(string repo, object based, object to)
public Compare(Repository repo, object based, object to)
{
_repo = repo;
_repo = repo.FullPath;
_canResetFiles = !repo.IsBare;
_based = GetSHA(based);
_to = GetSHA(to);
_baseName = GetName(based);
@@ -134,6 +141,121 @@ namespace SourceGit.ViewModels
new Commands.DiffTool(_repo, new Models.DiffOption(_based, _to, change)).Open();
}
public async Task ResetToLeftAsync(Models.Change change)
{
if (!_canResetFiles)
return;
if (change.Index == Models.ChangeState.Added)
{
var fullpath = Native.OS.GetAbsPath(_repo, change.Path);
if (File.Exists(fullpath))
await new Commands.Remove(_repo, [change.Path]).ExecAsync();
}
else if (change.Index == Models.ChangeState.Renamed)
{
var renamed = Native.OS.GetAbsPath(_repo, change.Path);
if (File.Exists(renamed))
await new Commands.Remove(_repo, [change.Path]).ExecAsync();
await new Commands.Checkout(_repo).FileWithRevisionAsync(change.OriginalPath, _baseHead.SHA);
}
else
{
await new Commands.Checkout(_repo).FileWithRevisionAsync(change.Path, _baseHead.SHA);
}
}
public async Task ResetToRightAsync(Models.Change change)
{
if (change.Index == Models.ChangeState.Deleted)
{
var fullpath = Native.OS.GetAbsPath(_repo, change.Path);
if (File.Exists(fullpath))
await new Commands.Remove(_repo, [change.Path]).ExecAsync();
}
else if (change.Index == Models.ChangeState.Renamed)
{
var old = Native.OS.GetAbsPath(_repo, change.OriginalPath);
if (File.Exists(old))
await new Commands.Remove(_repo, [change.OriginalPath]).ExecAsync();
await new Commands.Checkout(_repo).FileWithRevisionAsync(change.Path, ToHead.SHA);
}
else
{
await new Commands.Checkout(_repo).FileWithRevisionAsync(change.Path, ToHead.SHA);
}
}
public async Task ResetMultipleToLeftAsync(List<Models.Change> changes)
{
var checkouts = new List<string>();
var removes = new List<string>();
foreach (var c in changes)
{
if (c.Index == Models.ChangeState.Added)
{
var fullpath = Native.OS.GetAbsPath(_repo, c.Path);
if (File.Exists(fullpath))
removes.Add(c.Path);
}
else if (c.Index == Models.ChangeState.Renamed)
{
var old = Native.OS.GetAbsPath(_repo, c.Path);
if (File.Exists(old))
removes.Add(c.Path);
checkouts.Add(c.OriginalPath);
}
else
{
checkouts.Add(c.Path);
}
}
if (removes.Count > 0)
await new Commands.Remove(_repo, removes).ExecAsync();
if (checkouts.Count > 0)
await new Commands.Checkout(_repo).MultipleFilesWithRevisionAsync(checkouts, _baseHead.SHA);
}
public async Task ResetMultipleToRightAsync(List<Models.Change> changes)
{
var checkouts = new List<string>();
var removes = new List<string>();
foreach (var c in changes)
{
if (c.Index == Models.ChangeState.Deleted)
{
var fullpath = Native.OS.GetAbsPath(_repo, c.Path);
if (File.Exists(fullpath))
removes.Add(c.Path);
}
else if (c.Index == Models.ChangeState.Renamed)
{
var renamed = Native.OS.GetAbsPath(_repo, c.OriginalPath);
if (File.Exists(renamed))
removes.Add(c.OriginalPath);
checkouts.Add(c.Path);
}
else
{
checkouts.Add(c.Path);
}
}
if (removes.Count > 0)
await new Commands.Remove(_repo, removes).ExecAsync();
if (checkouts.Count > 0)
await new Commands.Checkout(_repo).MultipleFilesWithRevisionAsync(checkouts, _toHead.SHA);
}
public async Task SaveChangesAsPatchAsync(List<Models.Change> changes, string saveTo)
{
var succ = await Commands.SaveChangesAsPatch.ProcessRevisionCompareChangesAsync(_repo, changes, _based, _to, saveTo);
@@ -241,6 +363,7 @@ namespace SourceGit.ViewModels
private string _repo;
private bool _isLoading = true;
private bool _canResetFiles = false;
private string _based = string.Empty;
private string _to = string.Empty;
private string _baseName = string.Empty;

View File

@@ -58,7 +58,7 @@ namespace SourceGit.ViewModels
public void Launch()
{
if (_compareTo != null)
App.ShowWindow(new Compare(_repo.FullPath, _basedOn, _compareTo));
App.ShowWindow(new Compare(_repo, _basedOn, _compareTo));
_launcher?.CancelCommandPalette();
}

View File

@@ -528,7 +528,7 @@ namespace SourceGit.Views
compare.Icon = App.CreateMenuIcon("Icons.Compare");
compare.Click += (_, ev) =>
{
App.ShowWindow(new ViewModels.Compare(repo.FullPath, branches[0], branches[1]));
App.ShowWindow(new ViewModels.Compare(repo, branches[0], branches[1]));
ev.Handled = true;
};
menu.Items.Add(compare);
@@ -826,7 +826,7 @@ namespace SourceGit.Views
compareWithCurrent.Icon = App.CreateMenuIcon("Icons.Compare");
compareWithCurrent.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo.FullPath, branch, current));
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
};
var compareWith = new MenuItem();
@@ -1123,7 +1123,7 @@ namespace SourceGit.Views
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
compareWithHead.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo.FullPath, branch, current));
App.ShowWindow(new ViewModels.Compare(repo, branch, current));
};
var compareWith = new MenuItem();

View File

@@ -82,6 +82,34 @@ namespace SourceGit.Views
menu.Items.Add(explore);
}
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(patch);
if (vm.CanResetFiles)
{
var resetToLeft = new MenuItem();
resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName);
resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout");
resetToLeft.Click += async (_, ev) =>
{
await vm.ResetToLeftAsync(change);
ev.Handled = true;
};
var resetToRight = new MenuItem();
resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName);
resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout");
resetToRight.Click += async (_, ev) =>
{
await vm.ResetToRightAsync(change);
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(resetToLeft);
menu.Items.Add(resetToRight);
}
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
@@ -102,14 +130,39 @@ namespace SourceGit.Views
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(patch);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFullPath);
}
else
{
menu.Items.Add(patch);
if (vm.CanResetFiles)
{
var resetToLeft = new MenuItem();
resetToLeft.Header = App.Text("ChangeCM.ResetFileTo", vm.BaseName);
resetToLeft.Icon = App.CreateMenuIcon("Icons.File.Checkout");
resetToLeft.Click += async (_, ev) =>
{
await vm.ResetMultipleToLeftAsync(selected);
ev.Handled = true;
};
var resetToRight = new MenuItem();
resetToRight.Header = App.Text("ChangeCM.ResetFileTo", vm.ToName);
resetToRight.Icon = App.CreateMenuIcon("Icons.File.Checkout");
resetToRight.Click += async (_, ev) =>
{
await vm.ResetMultipleToRightAsync(selected);
ev.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(resetToLeft);
menu.Items.Add(resetToRight);
}
var copyPath = new MenuItem();
copyPath.Header = App.Text("CopyPath");
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
@@ -138,7 +191,6 @@ namespace SourceGit.Views
ev.Handled = true;
};
menu.Items.Add(patch);
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(copyPath);
menu.Items.Add(copyFullPath);

View File

@@ -262,7 +262,7 @@ namespace SourceGit.Views
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
compareWithHead.Click += (_, _) =>
{
App.ShowWindow(new ViewModels.Compare(repo.FullPath, tag, repo.CurrentBranch));
App.ShowWindow(new ViewModels.Compare(repo, tag, repo.CurrentBranch));
};
var compareWith = new MenuItem();
@@ -380,7 +380,7 @@ namespace SourceGit.Views
if (based.CreatorDate > to.CreatorDate)
(based, to) = (to, based);
App.ShowWindow(new ViewModels.Compare(repo.FullPath, based, to));
App.ShowWindow(new ViewModels.Compare(repo, based, to));
ev.Handled = true;
};
menu.Items.Add(compare);