using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { public class StashesPage : ObservableObject, IDisposable { public List Stashes { get => _stashes; set { if (SetProperty(ref _stashes, value)) RefreshVisible(); } } public List VisibleStashes { get => _visibleStashes; private set { if (SetProperty(ref _visibleStashes, value)) SelectedStash = null; } } public string SearchFilter { get => _searchFilter; set { if (SetProperty(ref _searchFilter, value)) RefreshVisible(); } } public Models.Stash SelectedStash { get => _selectedStash; set { if (SetProperty(ref _selectedStash, value)) { if (value == null) { Changes = null; _untracked.Clear(); } else { Task.Run(async () => { var changes = await new Commands.CompareRevisions(_repo.FullPath, $"{value.SHA}^", value.SHA) .ReadAsync() .ConfigureAwait(false); var untracked = new List(); if (value.Parents.Count == 3) { untracked = await new Commands.CompareRevisions(_repo.FullPath, value.UntrackedParent, value.Parents[2]) .ReadAsync() .ConfigureAwait(false); var needSort = changes.Count > 0 && untracked.Count > 0; changes.AddRange(untracked); if (needSort) changes.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); } Dispatcher.UIThread.Post(() => { if (value.SHA.Equals(_selectedStash?.SHA ?? string.Empty, StringComparison.Ordinal)) { _untracked = untracked; Changes = changes; } }); }); } } } } public List Changes { get => _changes; private set { if (SetProperty(ref _changes, value)) SelectedChanges = value is { Count: > 0 } ? [value[0]] : []; } } public List SelectedChanges { get => _selectedChanges; set { if (SetProperty(ref _selectedChanges, value)) { if (value is not { Count: 1 }) DiffContext = null; else if (_untracked.Contains(value[0])) DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], value[0]), _diffContext); else DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, value[0]), _diffContext); } } } public DiffContext DiffContext { get => _diffContext; private set => SetProperty(ref _diffContext, value); } public StashesPage(Repository repo) { _repo = repo; } public void Dispose() { _stashes?.Clear(); _changes?.Clear(); _selectedChanges?.Clear(); _untracked.Clear(); _repo = null; _selectedStash = null; _diffContext = null; } public void ClearSearchFilter() { SearchFilter = string.Empty; } public string GetAbsPath(string path) { return Native.OS.GetAbsPath(_repo.FullPath, path); } public void Apply(Models.Stash stash) { if (_repo.CanCreatePopup()) _repo.ShowPopup(new ApplyStash(_repo, stash)); } public void CheckoutBranch(Models.Stash stash) { if (_repo.CanCreatePopup()) _repo.ShowPopup(new CheckoutBranchFromStash(_repo, stash)); } public void Drop(Models.Stash stash) { if (_repo.CanCreatePopup()) _repo.ShowPopup(new DropStash(_repo, stash)); } public async Task SaveStashAsPatchAsync(Models.Stash stash, string saveTo) { var opts = new List(); var changes = await new Commands.CompareRevisions(_repo.FullPath, $"{stash.SHA}^", stash.SHA) .ReadAsync() .ConfigureAwait(false); foreach (var c in changes) opts.Add(new Models.DiffOption(stash.Parents[0], stash.SHA, c)); if (stash.Parents.Count == 3) { var untracked = await new Commands.CompareRevisions(_repo.FullPath, stash.UntrackedParent, stash.Parents[2]) .ReadAsync() .ConfigureAwait(false); foreach (var c in untracked) opts.Add(new Models.DiffOption(stash.UntrackedParent, stash.Parents[2], c)); changes.AddRange(untracked); } var succ = await Commands.SaveChangesAsPatch.ProcessStashChangesAsync(_repo.FullPath, opts, saveTo); if (succ) _repo.SendNotification(App.Text("SaveAsPatchSuccess")); } public void OpenChangeWithExternalDiffTool(Models.Change change) { Models.DiffOption opt; if (_untracked.Contains(change)) opt = new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], change); else opt = new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, change); new Commands.DiffTool(_repo.FullPath, opt).Open(); } public async Task CheckoutFilesAsync(List changes) { var untracked = new List(); var added = new List(); var modified = new List(); foreach (var c in changes) { if (_untracked.Contains(c) && _selectedStash.Parents.Count == 3) untracked.Add(c.Path); else if (c.Index == Models.ChangeState.Added && _selectedStash.Parents.Count > 1) added.Add(c.Path); else modified.Add(c.Path); } var log = _repo.CreateLog($"Reset File to '{_selectedStash.Name}'"); if (untracked.Count > 0) await new Commands.Checkout(_repo.FullPath) .Use(log) .MultipleFilesWithRevisionAsync(untracked, _selectedStash.Parents[2]); if (added.Count > 0) await new Commands.Checkout(_repo.FullPath) .Use(log) .MultipleFilesWithRevisionAsync(added, _selectedStash.Parents[1]); if (modified.Count > 0) await new Commands.Checkout(_repo.FullPath) .Use(log) .MultipleFilesWithRevisionAsync(modified, _selectedStash.SHA); log.Complete(); } public async Task ApplySelectedChanges(List changes) { if (_selectedStash == null) return; var opts = new List(); foreach (var c in changes) { if (_untracked.Contains(c) && _selectedStash.Parents.Count == 3) opts.Add(new Models.DiffOption(_selectedStash.UntrackedParent, _selectedStash.Parents[2], c)); else opts.Add(new Models.DiffOption(_selectedStash.Parents[0], _selectedStash.SHA, c)); } var saveTo = Path.GetTempFileName(); var succ = await Commands.SaveChangesAsPatch.ProcessStashChangesAsync(_repo.FullPath, opts, saveTo); if (!succ) return; var log = _repo.CreateLog($"Apply changes from '{_selectedStash.Name}'"); await new Commands.Apply(_repo.FullPath, saveTo, true, string.Empty, string.Empty) .Use(log) .ExecAsync(); log.Complete(); File.Delete(saveTo); } private void RefreshVisible() { if (string.IsNullOrEmpty(_searchFilter)) { VisibleStashes = _stashes; } else { var visible = new List(); foreach (var s in _stashes) { if (s.Message.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) visible.Add(s); } VisibleStashes = visible; } } private Repository _repo = null; private List _stashes = []; private List _visibleStashes = []; private string _searchFilter = string.Empty; private Models.Stash _selectedStash = null; private List _changes = null; private List _untracked = []; private List _selectedChanges = []; private DiffContext _diffContext = null; } }