2024-03-17 18:37:06 -07:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2024-02-28 19:29:54 -08:00
|
|
|
|
using Avalonia.Controls;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
using Avalonia.Platform.Storage;
|
|
|
|
|
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
|
|
|
|
|
{
|
2024-04-28 19:54:29 -07:00
|
|
|
|
public class ConflictContext : ObservableObject
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-28 19:54:29 -07:00
|
|
|
|
public bool IsResolved
|
|
|
|
|
{
|
|
|
|
|
get => _isResolved;
|
|
|
|
|
set => SetProperty(ref _isResolved, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ConflictContext(string repo, Models.Change change)
|
|
|
|
|
{
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
var result = new Commands.IsConflictResolved(repo, change).Result();
|
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
|
|
|
{
|
|
|
|
|
IsResolved = result;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool _isResolved = false;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public class WorkingCopy : ObservableObject
|
|
|
|
|
{
|
2024-05-30 00:13:59 -07:00
|
|
|
|
public bool IncludeUntracked
|
|
|
|
|
{
|
|
|
|
|
get => _repo.IncludeUntracked;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_repo.IncludeUntracked != value)
|
|
|
|
|
{
|
|
|
|
|
_repo.IncludeUntracked = value;
|
|
|
|
|
OnPropertyChanged(nameof(IncludeUntracked));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CanCommitWithPush
|
|
|
|
|
{
|
|
|
|
|
get => _canCommitWithPush;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (SetProperty(ref _canCommitWithPush, value))
|
|
|
|
|
OnPropertyChanged(nameof(IsCommitWithPushVisible));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsStaging
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _isStaging;
|
|
|
|
|
private set => SetProperty(ref _isStaging, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsUnstaging
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _isUnstaging;
|
|
|
|
|
private set => SetProperty(ref _isUnstaging, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool IsCommitting
|
|
|
|
|
{
|
|
|
|
|
get => _isCommitting;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private set => SetProperty(ref _isCommitting, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool UseAmend
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _useAmend;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (SetProperty(ref _useAmend, value) && value)
|
|
|
|
|
{
|
2024-06-06 21:31:10 -07:00
|
|
|
|
var currentBranch = _repo.Branches.Find(x => x.IsCurrent);
|
|
|
|
|
if (currentBranch == null)
|
2024-05-28 06:19:53 -07:00
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
|
|
|
|
|
_useAmend = false;
|
|
|
|
|
OnPropertyChanged();
|
2024-06-06 21:31:10 -07:00
|
|
|
|
return;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
}
|
2024-06-06 21:31:10 -07:00
|
|
|
|
|
|
|
|
|
var head = new Commands.QuerySingleCommit(_repo.FullPath, currentBranch.Head).Result();
|
|
|
|
|
if (head == null)
|
2024-05-28 06:19:53 -07:00
|
|
|
|
{
|
2024-06-06 21:31:10 -07:00
|
|
|
|
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
|
|
|
|
|
_useAmend = false;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
return;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
}
|
2024-06-06 21:31:10 -07:00
|
|
|
|
|
|
|
|
|
CommitMessage = head.Body;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
}
|
2024-05-30 00:13:59 -07:00
|
|
|
|
|
|
|
|
|
OnPropertyChanged(nameof(IsCommitWithPushVisible));
|
2024-05-28 06:19:53 -07:00
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-30 00:13:59 -07:00
|
|
|
|
public bool IsCommitWithPushVisible
|
|
|
|
|
{
|
|
|
|
|
get => !UseAmend && CanCommitWithPush;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public List<Models.Change> Unstaged
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _unstaged;
|
|
|
|
|
private set => SetProperty(ref _unstaged, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public List<Models.Change> Staged
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _staged;
|
|
|
|
|
private set => SetProperty(ref _staged, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public List<Models.Change> SelectedUnstaged
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
get => _selectedUnstaged;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
set
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (SetProperty(ref _selectedUnstaged, value))
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (value == null || value.Count == 0)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedStaged == null || _selectedStaged.Count == 0)
|
|
|
|
|
SetDetail(null);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
SelectedStaged = null;
|
2024-02-27 02:26:05 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (value.Count == 1)
|
|
|
|
|
SetDetail(value[0]);
|
|
|
|
|
else
|
|
|
|
|
SetDetail(null);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public List<Models.Change> SelectedStaged
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
get => _selectedStaged;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
set
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (SetProperty(ref _selectedStaged, value))
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (value == null || value.Count == 0)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
|
|
|
|
|
SetDetail(null);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
SelectedUnstaged = null;
|
2024-02-27 02:26:05 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (value.Count == 1)
|
|
|
|
|
SetDetail(value[0]);
|
|
|
|
|
else
|
|
|
|
|
SetDetail(null);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public int Count => _count;
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public object DetailContext
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _detailContext;
|
|
|
|
|
private set => SetProperty(ref _detailContext, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public string CommitMessage
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
get => _commitMessage;
|
|
|
|
|
set => SetProperty(ref _commitMessage, value);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public WorkingCopy(Repository repo)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo = repo;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void Cleanup()
|
|
|
|
|
{
|
2024-02-20 02:27:59 -08:00
|
|
|
|
_repo = null;
|
2024-05-30 00:13:59 -07:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedUnstaged != null)
|
2024-05-30 00:13:59 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
_selectedUnstaged.Clear();
|
2024-05-30 00:13:59 -07:00
|
|
|
|
OnPropertyChanged(nameof(SelectedUnstaged));
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedStaged != null)
|
2024-05-30 00:13:59 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
_selectedStaged.Clear();
|
2024-05-30 00:13:59 -07:00
|
|
|
|
OnPropertyChanged(nameof(SelectedStaged));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_unstaged != null)
|
|
|
|
|
{
|
|
|
|
|
_unstaged.Clear();
|
|
|
|
|
OnPropertyChanged(nameof(Unstaged));
|
|
|
|
|
}
|
2024-05-31 07:37:36 -07:00
|
|
|
|
|
2024-05-30 00:13:59 -07:00
|
|
|
|
if (_staged != null)
|
|
|
|
|
{
|
|
|
|
|
_staged.Clear();
|
|
|
|
|
OnPropertyChanged(nameof(Staged));
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-20 02:27:59 -08:00
|
|
|
|
_detailContext = null;
|
|
|
|
|
_commitMessage = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public bool SetData(List<Models.Change> changes)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var unstaged = new List<Models.Change>();
|
|
|
|
|
var staged = new List<Models.Change>();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var selectedUnstaged = new List<Models.Change>();
|
|
|
|
|
var selectedStaged = new List<Models.Change>();
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var lastSelectedUnstaged = new HashSet<string>();
|
|
|
|
|
var lastSelectedStaged = new HashSet<string>();
|
|
|
|
|
if (_selectedUnstaged != null)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
foreach (var c in _selectedUnstaged)
|
|
|
|
|
lastSelectedUnstaged.Add(c.Path);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
2024-05-28 06:19:53 -07:00
|
|
|
|
else if (_selectedStaged != null)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
foreach (var c in _selectedStaged)
|
|
|
|
|
lastSelectedStaged.Add(c.Path);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var hasConflict = false;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
foreach (var c in changes)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
if (c.Index == Models.ChangeState.Modified
|
|
|
|
|
|| c.Index == Models.ChangeState.Added
|
|
|
|
|
|| c.Index == Models.ChangeState.Deleted
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|| c.Index == Models.ChangeState.Renamed)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
staged.Add(c);
|
2024-05-28 06:19:53 -07:00
|
|
|
|
|
|
|
|
|
if (lastSelectedStaged.Contains(c.Path))
|
|
|
|
|
selectedStaged.Add(c);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (c.WorkTree != Models.ChangeState.None)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
unstaged.Add(c);
|
|
|
|
|
hasConflict |= c.IsConflit;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
|
|
|
|
|
if (lastSelectedUnstaged.Contains(c.Path))
|
|
|
|
|
selectedUnstaged.Add(c);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_count = changes.Count;
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
Dispatcher.UIThread.Invoke(() =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_isLoadingData = true;
|
|
|
|
|
Unstaged = unstaged;
|
|
|
|
|
Staged = staged;
|
|
|
|
|
_isLoadingData = false;
|
2024-02-27 02:26:05 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (selectedUnstaged.Count > 0)
|
|
|
|
|
SelectedUnstaged = selectedUnstaged;
|
|
|
|
|
else if (selectedStaged.Count > 0)
|
|
|
|
|
SelectedStaged = selectedStaged;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
else
|
2024-05-28 06:19:53 -07:00
|
|
|
|
SetDetail(null);
|
|
|
|
|
|
2024-04-26 06:05:00 -07:00
|
|
|
|
// Try to load merge message from MERGE_MSG
|
|
|
|
|
if (string.IsNullOrEmpty(_commitMessage))
|
|
|
|
|
{
|
|
|
|
|
var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG");
|
|
|
|
|
if (File.Exists(mergeMsgFile))
|
|
|
|
|
CommitMessage = File.ReadAllText(mergeMsgFile);
|
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return hasConflict;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public void OpenAssumeUnchanged()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var dialog = new Views.AssumeUnchangedManager()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
DataContext = new AssumeUnchangedManager(_repo.FullPath)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
dialog.ShowDialog(App.GetTopLevel() as Window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StageSelected()
|
|
|
|
|
{
|
|
|
|
|
StageChanges(_selectedUnstaged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StageAll()
|
|
|
|
|
{
|
|
|
|
|
StageChanges(_unstaged);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public async void StageChanges(List<Models.Change> changes)
|
|
|
|
|
{
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (_unstaged.Count == 0 || changes.Count == 0)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
SetDetail(null);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
IsStaging = true;
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (changes.Count == _unstaged.Count)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
await Task.Run(() => new Commands.Add(_repo.FullPath).Exec());
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < changes.Count; i += 10)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var count = Math.Min(10, changes.Count - i);
|
|
|
|
|
var step = changes.GetRange(i, count);
|
|
|
|
|
await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec());
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-07 17:57:29 -08:00
|
|
|
|
_repo.MarkWorkingCopyDirtyManually();
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
IsStaging = false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public void UnstageSelected()
|
|
|
|
|
{
|
|
|
|
|
UnstageChanges(_selectedStaged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UnstageAll()
|
|
|
|
|
{
|
|
|
|
|
UnstageChanges(_staged);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public async void UnstageChanges(List<Models.Change> changes)
|
|
|
|
|
{
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (_staged.Count == 0 || changes.Count == 0)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
SetDetail(null);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
IsUnstaging = true;
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (changes.Count == _staged.Count)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec());
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < changes.Count; i += 10)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var count = Math.Min(10, changes.Count - i);
|
|
|
|
|
var step = changes.GetRange(i, count);
|
|
|
|
|
await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Exec());
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-07 17:57:29 -08:00
|
|
|
|
_repo.MarkWorkingCopyDirtyManually();
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
IsUnstaging = false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public void Discard(List<Models.Change> changes, bool isUnstaged)
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
|
|
|
|
if (isUnstaged)
|
|
|
|
|
{
|
|
|
|
|
if (changes.Count == _unstaged.Count && _staged.Count == 0)
|
|
|
|
|
{
|
2024-02-29 19:34:32 -08:00
|
|
|
|
PopupHost.ShowPopup(new Discard(_repo));
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-02-29 19:34:32 -08:00
|
|
|
|
PopupHost.ShowPopup(new Discard(_repo, changes, true));
|
|
|
|
|
}
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (changes.Count == _staged.Count && _unstaged.Count == 0)
|
|
|
|
|
{
|
2024-02-29 19:34:32 -08:00
|
|
|
|
PopupHost.ShowPopup(new Discard(_repo));
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-02-29 19:34:32 -08:00
|
|
|
|
PopupHost.ShowPopup(new Discard(_repo, changes, false));
|
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public void Commit()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
DoCommit(false);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public void CommitWithPush()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
DoCommit(true);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public ContextMenu CreateContextMenuForUnstagedChanges()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedUnstaged.Count == 0)
|
2024-03-31 01:54:29 -07:00
|
|
|
|
return null;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
var menu = new ContextMenu();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedUnstaged.Count == 1)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var change = _selectedUnstaged[0];
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
|
|
|
|
|
|
|
|
|
var explore = new MenuItem();
|
|
|
|
|
explore.Header = App.Text("RevealFile");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
2024-02-05 23:08:37 -08:00
|
|
|
|
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
explore.Click += (_, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
Native.OS.OpenInFileManager(path, true);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-04-11 05:50:19 -07:00
|
|
|
|
menu.Items.Add(explore);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var openWith = new MenuItem();
|
|
|
|
|
openWith.Header = App.Text("OpenWith");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
2024-02-05 23:08:37 -08:00
|
|
|
|
openWith.IsEnabled = File.Exists(path);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
openWith.Click += (_, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
Native.OS.OpenWithDefaultEditor(path);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-04-11 05:50:19 -07:00
|
|
|
|
menu.Items.Add(openWith);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
if (change.IsConflit)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var useTheirs = new MenuItem();
|
|
|
|
|
useTheirs.Icon = App.CreateMenuIcon("Icons.Incoming");
|
|
|
|
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
|
|
|
|
useTheirs.Click += (_, e) =>
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UseTheirs(_selectedUnstaged);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-04-13 04:24:45 -07:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var useMine = new MenuItem();
|
|
|
|
|
useMine.Icon = App.CreateMenuIcon("Icons.Local");
|
|
|
|
|
useMine.Header = App.Text("FileCM.UseMine");
|
|
|
|
|
useMine.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UseMine(_selectedUnstaged);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var openMerger = new MenuItem();
|
|
|
|
|
openMerger.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
|
|
|
|
openMerger.Header = App.Text("FileCM.OpenWithExternalMerger");
|
|
|
|
|
openMerger.Click += (_, e) =>
|
|
|
|
|
{
|
2024-04-28 19:54:29 -07:00
|
|
|
|
UseExternalMergeTool(change);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(useTheirs);
|
|
|
|
|
menu.Items.Add(useMine);
|
|
|
|
|
menu.Items.Add(openMerger);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
}
|
|
|
|
|
else
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var stage = new MenuItem();
|
|
|
|
|
stage.Header = App.Text("FileCM.Stage");
|
|
|
|
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
|
|
|
|
stage.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
StageChanges(_selectedUnstaged);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var discard = new MenuItem();
|
|
|
|
|
discard.Header = App.Text("FileCM.Discard");
|
|
|
|
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
|
|
|
|
discard.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
Discard(_selectedUnstaged, true);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var stash = new MenuItem();
|
|
|
|
|
stash.Header = App.Text("FileCM.Stash");
|
|
|
|
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
|
|
|
|
stash.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
2024-04-11 05:50:19 -07:00
|
|
|
|
}
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var patch = new MenuItem();
|
|
|
|
|
patch.Header = App.Text("FileCM.SaveAsPatch");
|
|
|
|
|
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
|
|
|
|
patch.Click += async (_, e) =>
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var topLevel = App.GetTopLevel();
|
|
|
|
|
if (topLevel == null)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var options = new FilePickerSaveOptions();
|
|
|
|
|
options.Title = App.Text("FileCM.SaveAsPatch");
|
|
|
|
|
options.DefaultExtension = ".patch";
|
|
|
|
|
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
|
|
|
|
if (storageFile != null)
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
2024-04-11 05:50:19 -07:00
|
|
|
|
if (succ)
|
|
|
|
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var history = new MenuItem();
|
|
|
|
|
history.Header = App.Text("FileHistory");
|
|
|
|
|
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
|
|
|
|
history.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo.FullPath, change.Path) };
|
|
|
|
|
window.Show();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var assumeUnchanged = new MenuItem();
|
|
|
|
|
assumeUnchanged.Header = App.Text("FileCM.AssumeUnchanged");
|
|
|
|
|
assumeUnchanged.Icon = App.CreateMenuIcon("Icons.File.Ignore");
|
|
|
|
|
assumeUnchanged.IsEnabled = change.WorkTree != Models.ChangeState.Untracked;
|
|
|
|
|
assumeUnchanged.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
new Commands.AssumeUnchanged(_repo.FullPath).Add(change.Path);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(stage);
|
|
|
|
|
menu.Items.Add(discard);
|
|
|
|
|
menu.Items.Add(stash);
|
|
|
|
|
menu.Items.Add(patch);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
menu.Items.Add(history);
|
|
|
|
|
menu.Items.Add(assumeUnchanged);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
}
|
2024-04-13 04:24:45 -07:00
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var copy = new MenuItem();
|
|
|
|
|
copy.Header = App.Text("CopyPath");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
copy.Click += (_, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
App.CopyText(change.Path);
|
|
|
|
|
e.Handled = true;
|
2024-04-13 04:24:45 -07:00
|
|
|
|
};
|
2024-02-05 23:08:37 -08:00
|
|
|
|
menu.Items.Add(copy);
|
2024-05-31 21:34:16 -07:00
|
|
|
|
|
|
|
|
|
var copyFileName = new MenuItem();
|
|
|
|
|
copyFileName.Header = App.Text("CopyFileName");
|
|
|
|
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
2024-05-31 07:37:36 -07:00
|
|
|
|
copyFileName.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
App.CopyText(Path.GetFileName(change.Path));
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
menu.Items.Add(copyFileName);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-04-11 05:50:19 -07:00
|
|
|
|
var hasConflicts = false;
|
|
|
|
|
var hasNoneConflicts = false;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
foreach (var change in _selectedUnstaged)
|
2024-04-11 05:50:19 -07:00
|
|
|
|
{
|
|
|
|
|
if (change.IsConflit)
|
|
|
|
|
{
|
|
|
|
|
hasConflicts = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hasNoneConflicts = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasConflicts)
|
|
|
|
|
{
|
|
|
|
|
if (hasNoneConflicts)
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "You have selected both non-conflict changes with conflicts!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var useTheirs = new MenuItem();
|
|
|
|
|
useTheirs.Icon = App.CreateMenuIcon("Icons.Incoming");
|
|
|
|
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
|
|
|
|
useTheirs.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UseTheirs(_selectedUnstaged);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var useMine = new MenuItem();
|
|
|
|
|
useMine.Icon = App.CreateMenuIcon("Icons.Local");
|
|
|
|
|
useMine.Header = App.Text("FileCM.UseMine");
|
|
|
|
|
useMine.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UseMine(_selectedUnstaged);
|
2024-04-11 05:50:19 -07:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(useTheirs);
|
|
|
|
|
menu.Items.Add(useMine);
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var stage = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
stage.Header = App.Text("FileCM.StageMulti", _selectedUnstaged.Count);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
stage.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
StageChanges(_selectedUnstaged);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var discard = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedUnstaged.Count);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
discard.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
Discard(_selectedUnstaged, true);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var stash = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
stash.Header = App.Text("FileCM.StashMulti", _selectedUnstaged.Count);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
stash.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var patch = new MenuItem();
|
|
|
|
|
patch.Header = App.Text("FileCM.SaveAsPatch");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
patch.Click += async (o, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var topLevel = App.GetTopLevel();
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (topLevel == null)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
var options = new FilePickerSaveOptions();
|
|
|
|
|
options.Title = App.Text("FileCM.SaveAsPatch");
|
|
|
|
|
options.DefaultExtension = ".patch";
|
|
|
|
|
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
2024-03-17 18:37:06 -07:00
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (storageFile != null)
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (succ)
|
|
|
|
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(stage);
|
|
|
|
|
menu.Items.Add(discard);
|
|
|
|
|
menu.Items.Add(stash);
|
|
|
|
|
menu.Items.Add(patch);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
public ContextMenu CreateContextMenuForStagedChanges()
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedStaged.Count == 0)
|
2024-03-31 01:54:29 -07:00
|
|
|
|
return null;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
var menu = new ContextMenu();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
if (_selectedStaged.Count == 1)
|
2024-03-17 18:37:06 -07:00
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var change = _selectedStaged[0];
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
|
|
|
|
|
|
|
|
|
var explore = new MenuItem();
|
|
|
|
|
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
|
|
|
|
explore.Header = App.Text("RevealFile");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
explore.Click += (o, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
Native.OS.OpenInFileManager(path, true);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var openWith = new MenuItem();
|
|
|
|
|
openWith.Header = App.Text("OpenWith");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
2024-02-05 23:08:37 -08:00
|
|
|
|
openWith.IsEnabled = File.Exists(path);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
openWith.Click += (_, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
Native.OS.OpenWithDefaultEditor(path);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var unstage = new MenuItem();
|
|
|
|
|
unstage.Header = App.Text("FileCM.Unstage");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
unstage.Click += (o, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UnstageChanges(_selectedStaged);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-29 19:34:32 -08:00
|
|
|
|
var discard = new MenuItem();
|
|
|
|
|
discard.Header = App.Text("FileCM.Discard");
|
|
|
|
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
discard.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
Discard(_selectedStaged, false);
|
2024-02-29 19:34:32 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var stash = new MenuItem();
|
|
|
|
|
stash.Header = App.Text("FileCM.Stash");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
stash.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var patch = new MenuItem();
|
|
|
|
|
patch.Header = App.Text("FileCM.SaveAsPatch");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
patch.Click += async (o, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var topLevel = App.GetTopLevel();
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (topLevel == null)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
var options = new FilePickerSaveOptions();
|
|
|
|
|
options.Title = App.Text("FileCM.SaveAsPatch");
|
|
|
|
|
options.DefaultExtension = ".patch";
|
|
|
|
|
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
|
|
|
|
|
|
|
|
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (storageFile != null)
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (succ)
|
|
|
|
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var copyPath = new MenuItem();
|
|
|
|
|
copyPath.Header = App.Text("CopyPath");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
copyPath.Click += (o, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
App.CopyText(change.Path);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-06-06 00:31:02 -07:00
|
|
|
|
|
2024-05-31 21:34:16 -07:00
|
|
|
|
var copyFileName = new MenuItem();
|
|
|
|
|
copyFileName.Header = App.Text("CopyFileName");
|
|
|
|
|
copyFileName.Icon = App.CreateMenuIcon("Icons.Copy");
|
|
|
|
|
copyFileName.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
App.CopyText(Path.GetFileName(change.Path));
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
menu.Items.Add(explore);
|
|
|
|
|
menu.Items.Add(openWith);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
menu.Items.Add(unstage);
|
2024-02-29 19:34:32 -08:00
|
|
|
|
menu.Items.Add(discard);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
menu.Items.Add(stash);
|
|
|
|
|
menu.Items.Add(patch);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
menu.Items.Add(copyPath);
|
2024-05-31 21:34:16 -07:00
|
|
|
|
menu.Items.Add(copyFileName);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var unstage = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
unstage.Click += (o, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
UnstageChanges(_selectedStaged);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-29 19:34:32 -08:00
|
|
|
|
var discard = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedStaged.Count);
|
2024-02-29 19:34:32 -08:00
|
|
|
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
discard.Click += (_, e) =>
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
Discard(_selectedStaged, false);
|
2024-02-29 19:34:32 -08:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var stash = new MenuItem();
|
2024-05-28 06:19:53 -07:00
|
|
|
|
stash.Header = App.Text("FileCM.StashMulti", _selectedStaged.Count);
|
2024-02-27 02:26:05 -08:00
|
|
|
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
stash.Click += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var patch = new MenuItem();
|
|
|
|
|
patch.Header = App.Text("FileCM.SaveAsPatch");
|
2024-02-27 02:26:05 -08:00
|
|
|
|
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
2024-03-17 18:37:06 -07:00
|
|
|
|
patch.Click += async (_, e) =>
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var topLevel = App.GetTopLevel();
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (topLevel == null)
|
|
|
|
|
return;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
|
|
|
|
|
var options = new FilePickerSaveOptions();
|
|
|
|
|
options.Title = App.Text("FileCM.SaveAsPatch");
|
|
|
|
|
options.DefaultExtension = ".patch";
|
|
|
|
|
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
|
|
|
|
|
|
|
|
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (storageFile != null)
|
|
|
|
|
{
|
2024-05-28 06:19:53 -07:00
|
|
|
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
2024-03-31 01:54:29 -07:00
|
|
|
|
if (succ)
|
|
|
|
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
2024-02-05 23:08:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(unstage);
|
2024-02-29 19:34:32 -08:00
|
|
|
|
menu.Items.Add(discard);
|
2024-02-05 23:08:37 -08:00
|
|
|
|
menu.Items.Add(stash);
|
|
|
|
|
menu.Items.Add(patch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
public ContextMenu CreateContextMenuForCommitMessages()
|
|
|
|
|
{
|
2024-02-17 23:44:05 -08:00
|
|
|
|
var menu = new ContextMenu();
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (_repo.CommitMessages.Count == 0)
|
|
|
|
|
{
|
2024-02-17 23:44:05 -08:00
|
|
|
|
var empty = new MenuItem();
|
|
|
|
|
empty.Header = App.Text("WorkingCopy.NoCommitHistories");
|
|
|
|
|
empty.IsEnabled = false;
|
|
|
|
|
menu.Items.Add(empty);
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tip = new MenuItem();
|
|
|
|
|
tip.Header = App.Text("WorkingCopy.HasCommitHistories");
|
|
|
|
|
tip.IsEnabled = false;
|
|
|
|
|
menu.Items.Add(tip);
|
|
|
|
|
menu.Items.Add(new MenuItem() { Header = "-" });
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
foreach (var message in _repo.CommitMessages)
|
|
|
|
|
{
|
2024-02-17 23:44:05 -08:00
|
|
|
|
var dump = message;
|
|
|
|
|
|
|
|
|
|
var item = new MenuItem();
|
|
|
|
|
item.Header = dump;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
item.Click += (o, e) =>
|
|
|
|
|
{
|
2024-02-17 23:44:05 -08:00
|
|
|
|
CommitMessage = dump;
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
menu.Items.Add(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 06:19:53 -07:00
|
|
|
|
private void SetDetail(Models.Change change)
|
|
|
|
|
{
|
|
|
|
|
if (_isLoadingData)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var isUnstaged = _selectedUnstaged != null && _selectedUnstaged.Count > 0;
|
|
|
|
|
if (change == null)
|
|
|
|
|
{
|
|
|
|
|
DetailContext = null;
|
|
|
|
|
}
|
|
|
|
|
else if (change.IsConflit && isUnstaged)
|
|
|
|
|
{
|
|
|
|
|
DetailContext = new ConflictContext(_repo.FullPath, change);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (_detailContext is DiffContext previous)
|
|
|
|
|
{
|
|
|
|
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void UseTheirs(List<Models.Change> changes)
|
|
|
|
|
{
|
|
|
|
|
var files = new List<string>();
|
|
|
|
|
foreach (var change in changes)
|
|
|
|
|
{
|
|
|
|
|
if (change.IsConflit)
|
|
|
|
|
files.Add(change.Path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
|
|
|
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
|
|
|
|
|
if (succ)
|
|
|
|
|
{
|
|
|
|
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
|
|
|
|
}
|
|
|
|
|
_repo.MarkWorkingCopyDirtyManually();
|
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void UseMine(List<Models.Change> changes)
|
|
|
|
|
{
|
|
|
|
|
var files = new List<string>();
|
|
|
|
|
foreach (var change in changes)
|
|
|
|
|
{
|
|
|
|
|
if (change.IsConflit)
|
|
|
|
|
files.Add(change.Path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
|
|
|
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
|
|
|
|
|
if (succ)
|
|
|
|
|
{
|
|
|
|
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
|
|
|
|
}
|
|
|
|
|
_repo.MarkWorkingCopyDirtyManually();
|
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void UseExternalMergeTool(Models.Change change)
|
|
|
|
|
{
|
|
|
|
|
var type = Preference.Instance.ExternalMergeToolType;
|
|
|
|
|
var exec = Preference.Instance.ExternalMergeToolPath;
|
|
|
|
|
|
|
|
|
|
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
|
|
|
|
if (tool == null)
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
|
|
|
|
|
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
|
|
|
|
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
|
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DoCommit(bool autoPush)
|
|
|
|
|
{
|
|
|
|
|
if (!PopupHost.CanCreatePopup())
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_staged.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_commitMessage))
|
|
|
|
|
{
|
|
|
|
|
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PushCommitMessage();
|
|
|
|
|
|
|
|
|
|
SetDetail(null);
|
|
|
|
|
IsCommitting = true;
|
|
|
|
|
_repo.SetWatcherEnabled(false);
|
|
|
|
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
var succ = new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec();
|
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
|
|
|
{
|
|
|
|
|
if (succ)
|
|
|
|
|
{
|
|
|
|
|
CommitMessage = string.Empty;
|
|
|
|
|
UseAmend = false;
|
|
|
|
|
|
|
|
|
|
if (autoPush)
|
|
|
|
|
{
|
|
|
|
|
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_repo.MarkWorkingCopyDirtyManually();
|
|
|
|
|
_repo.SetWatcherEnabled(true);
|
|
|
|
|
|
|
|
|
|
IsCommitting = false;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
private void PushCommitMessage()
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (existIdx == 0)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
return;
|
2024-03-17 18:37:06 -07:00
|
|
|
|
}
|
|
|
|
|
else if (existIdx > 0)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo.CommitMessages.Move(existIdx, 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 18:37:06 -07:00
|
|
|
|
if (_repo.CommitMessages.Count > 9)
|
|
|
|
|
{
|
2024-02-05 23:08:37 -08:00
|
|
|
|
_repo.CommitMessages.RemoveRange(9, _repo.CommitMessages.Count - 9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_repo.CommitMessages.Insert(0, CommitMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Repository _repo = null;
|
|
|
|
|
private bool _isLoadingData = false;
|
|
|
|
|
private bool _isStaging = false;
|
|
|
|
|
private bool _isUnstaging = false;
|
|
|
|
|
private bool _isCommitting = false;
|
|
|
|
|
private bool _useAmend = false;
|
2024-05-30 00:13:59 -07:00
|
|
|
|
private bool _canCommitWithPush = false;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private List<Models.Change> _unstaged = null;
|
|
|
|
|
private List<Models.Change> _staged = null;
|
2024-05-28 06:19:53 -07:00
|
|
|
|
private List<Models.Change> _selectedUnstaged = null;
|
|
|
|
|
private List<Models.Change> _selectedStaged = null;
|
2024-02-05 23:08:37 -08:00
|
|
|
|
private int _count = 0;
|
|
|
|
|
private object _detailContext = null;
|
|
|
|
|
private string _commitMessage = string.Empty;
|
|
|
|
|
}
|
2024-03-31 01:54:29 -07:00
|
|
|
|
}
|