mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-05-22 20:54:59 +00:00
style: add .editorconfig for code formatting. see issu #25
This commit is contained in:
parent
a8eeea4f78
commit
18aaa0a143
225 changed files with 7781 additions and 3911 deletions
|
@ -1,42 +1,52 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class AddRemote : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class AddRemote : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Remote name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad remote name format!!!")]
|
||||
[CustomValidation(typeof(AddRemote), nameof(ValidateRemoteName))]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Remote URL is required!!!")]
|
||||
[CustomValidation(typeof(AddRemote), nameof(ValidateRemoteURL))]
|
||||
public string Url {
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _url, value, true)) UseSSH = Models.Remote.IsSSH(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseSSH {
|
||||
public bool UseSSH
|
||||
{
|
||||
get => _useSSH;
|
||||
set => SetProperty(ref _useSSH, value);
|
||||
}
|
||||
|
||||
public string SSHKey {
|
||||
public string SSHKey
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public AddRemote(Repository repo) {
|
||||
public AddRemote(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
View = new Views.AddRemote() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is AddRemote add) {
|
||||
public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is AddRemote add)
|
||||
{
|
||||
var exists = add._repo.Remotes.Find(x => x.Name == name);
|
||||
if (exists != null) return new ValidationResult("A remote with given name already exists!!!");
|
||||
}
|
||||
|
@ -44,8 +54,10 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is AddRemote add) {
|
||||
public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is AddRemote add)
|
||||
{
|
||||
if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Bad remote URL format!!!");
|
||||
|
||||
var exists = add._repo.Remotes.Find(x => x.URL == url);
|
||||
|
@ -55,22 +67,27 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Adding remote ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Remote(_repo.FullPath).Add(_name, _url);
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
SetProgressDescription("Fetching from added remote ...");
|
||||
new Commands.Fetch(_repo.FullPath, _name, true, SetProgressDescription).Exec();
|
||||
|
||||
if (_useSSH) {
|
||||
if (_useSSH)
|
||||
{
|
||||
SetProgressDescription("Post processing ...");
|
||||
new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", SSHKey);
|
||||
}
|
||||
}
|
||||
CallUIThread(() => {
|
||||
CallUIThread(() =>
|
||||
{
|
||||
_repo.MarkBranchesDirtyManually();
|
||||
_repo.SetWatcherEnabled(true);
|
||||
});
|
||||
|
@ -78,9 +95,9 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _name = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private bool _useSSH = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,62 +2,74 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class AddSubmodule : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class AddSubmodule : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Url is required!!!")]
|
||||
[CustomValidation(typeof(AddSubmodule), nameof(ValidateURL))]
|
||||
public string Url {
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set => SetProperty(ref _url, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Reletive path is required!!!")]
|
||||
[CustomValidation(typeof(AddSubmodule), nameof(ValidateRelativePath))]
|
||||
public string RelativePath {
|
||||
public string RelativePath
|
||||
{
|
||||
get => _relativePath;
|
||||
set => SetProperty(ref _relativePath, value, true);
|
||||
}
|
||||
|
||||
public bool Recursive {
|
||||
public bool Recursive
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public AddSubmodule(Repository repo) {
|
||||
public AddSubmodule(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
View = new Views.AddSubmodule() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateURL(string url, ValidationContext ctx) {
|
||||
public static ValidationResult ValidateURL(string url, ValidationContext ctx)
|
||||
{
|
||||
if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Invalid repository URL format");
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRelativePath(string path, ValidationContext ctx) {
|
||||
if (Path.Exists(path)) {
|
||||
public static ValidationResult ValidateRelativePath(string path, ValidationContext ctx)
|
||||
{
|
||||
if (Path.Exists(path))
|
||||
{
|
||||
return new ValidationResult("Give path is exists already!");
|
||||
}
|
||||
|
||||
if (Path.IsPathRooted(path)) {
|
||||
if (Path.IsPathRooted(path))
|
||||
{
|
||||
return new ValidationResult("Path must be relative to this repository!");
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Adding submodule...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Submodule(_repo.FullPath).Add(_url, _relativePath, Recursive, SetProgressDescription);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _url = string.Empty;
|
||||
private string _relativePath = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,31 +3,38 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Apply : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Apply : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Patch file is required!!!")]
|
||||
[CustomValidation(typeof(Apply), nameof(ValidatePatchFile))]
|
||||
public string PatchFile {
|
||||
public string PatchFile
|
||||
{
|
||||
get => _patchFile;
|
||||
set => SetProperty(ref _patchFile, value, true);
|
||||
}
|
||||
|
||||
public bool IgnoreWhiteSpace {
|
||||
public bool IgnoreWhiteSpace
|
||||
{
|
||||
get => _ignoreWhiteSpace;
|
||||
set => SetProperty(ref _ignoreWhiteSpace, value);
|
||||
}
|
||||
|
||||
public List<Models.ApplyWhiteSpaceMode> WhiteSpaceModes {
|
||||
public List<Models.ApplyWhiteSpaceMode> WhiteSpaceModes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.ApplyWhiteSpaceMode SelectedWhiteSpaceMode {
|
||||
public Models.ApplyWhiteSpaceMode SelectedWhiteSpaceMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Apply(Repository repo) {
|
||||
public Apply(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
WhiteSpaceModes = new List<Models.ApplyWhiteSpaceMode> {
|
||||
|
@ -41,27 +48,31 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Apply() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidatePatchFile(string file, ValidationContext _) {
|
||||
if (File.Exists(file)) {
|
||||
public static ValidationResult ValidatePatchFile(string file, ValidationContext _)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
return new ValidationResult($"File '{file}' can NOT be found!!!");
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Apply patch...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg, null).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _patchFile = string.Empty;
|
||||
private bool _ignoreWhiteSpace = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,21 +2,26 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Archive : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Archive : Popup
|
||||
{
|
||||
|
||||
[Required(ErrorMessage = "Output file name is required")]
|
||||
public string SaveFile {
|
||||
public string SaveFile
|
||||
{
|
||||
get => _saveFile;
|
||||
set => SetProperty(ref _saveFile, value, true);
|
||||
}
|
||||
|
||||
public object BasedOn {
|
||||
public object BasedOn
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Archive(Repository repo, Models.Branch branch) {
|
||||
public Archive(Repository repo, Models.Branch branch)
|
||||
{
|
||||
_repo = repo;
|
||||
_revision = branch.Head;
|
||||
_saveFile = $"archive-{Path.GetFileNameWithoutExtension(branch.Name)}.zip";
|
||||
|
@ -24,15 +29,17 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Archive() { DataContext = this };
|
||||
}
|
||||
|
||||
public Archive(Repository repo, Models.Commit commit) {
|
||||
public Archive(Repository repo, Models.Commit commit)
|
||||
{
|
||||
_repo = repo;
|
||||
_revision = commit.SHA;
|
||||
_saveFile = $"archive-{commit.SHA.Substring(0,10)}.zip";
|
||||
_saveFile = $"archive-{commit.SHA.Substring(0, 10)}.zip";
|
||||
BasedOn = commit;
|
||||
View = new Views.Archive() { DataContext = this };
|
||||
}
|
||||
|
||||
public Archive(Repository repo, Models.Tag tag) {
|
||||
public Archive(Repository repo, Models.Tag tag)
|
||||
{
|
||||
_repo = repo;
|
||||
_revision = tag.SHA;
|
||||
_saveFile = $"archive-{tag.Name}.zip";
|
||||
|
@ -40,13 +47,16 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Archive() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Archiving ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Archive(_repo.FullPath, _revision, _saveFile, SetProgressDescription).Exec();
|
||||
CallUIThread(() => {
|
||||
CallUIThread(() =>
|
||||
{
|
||||
_repo.SetWatcherEnabled(true);
|
||||
if (succ) App.SendNotification(_repo.FullPath, $"Save archive to : {_saveFile}");
|
||||
});
|
||||
|
@ -55,8 +65,8 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _saveFile = string.Empty;
|
||||
private string _revision = string.Empty;
|
||||
private readonly string _revision = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +1,38 @@
|
|||
using Avalonia.Collections;
|
||||
using Avalonia.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class AssumeUnchangedManager {
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class AssumeUnchangedManager
|
||||
{
|
||||
public AvaloniaList<string> Files { get; private set; }
|
||||
|
||||
public AssumeUnchangedManager(string repo) {
|
||||
public AssumeUnchangedManager(string repo)
|
||||
{
|
||||
_repo = repo;
|
||||
Files = new AvaloniaList<string>();
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var collect = new Commands.AssumeUnchanged(_repo).View();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Files.AddRange(collect);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void Remove(object param) {
|
||||
if (param is string file) {
|
||||
public void Remove(object param)
|
||||
{
|
||||
if (param is string file)
|
||||
{
|
||||
new Commands.AssumeUnchanged(_repo).Remove(file);
|
||||
Files.Remove(file);
|
||||
}
|
||||
}
|
||||
|
||||
private string _repo;
|
||||
private readonly string _repo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +1,60 @@
|
|||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Blame : ObservableObject {
|
||||
public string Title {
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Blame : ObservableObject
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string SelectedSHA {
|
||||
public string SelectedSHA
|
||||
{
|
||||
get => _selectedSHA;
|
||||
private set => SetProperty(ref _selectedSHA, value);
|
||||
}
|
||||
|
||||
public bool IsBinary {
|
||||
public bool IsBinary
|
||||
{
|
||||
get => _data != null && _data.IsBinary;
|
||||
}
|
||||
|
||||
public Models.BlameData Data {
|
||||
public Models.BlameData Data
|
||||
{
|
||||
get => _data;
|
||||
private set => SetProperty(ref _data, value);
|
||||
}
|
||||
|
||||
public Blame(string repo, string file, string revision) {
|
||||
public Blame(string repo, string file, string revision)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
Title = $"{file} @ {revision.Substring(0, 10)}";
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var result = new Commands.Blame(repo, file, revision).Result();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Data = result;
|
||||
OnPropertyChanged(nameof(IsBinary));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void NavigateToCommit(string commitSHA) {
|
||||
public void NavigateToCommit(string commitSHA)
|
||||
{
|
||||
var repo = Preference.FindRepository(_repo);
|
||||
if (repo != null) repo.NavigateToCommit(commitSHA);
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private readonly string _repo = string.Empty;
|
||||
private string _selectedSHA = string.Empty;
|
||||
private Models.BlameData _data = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Checkout : Popup {
|
||||
public string Branch {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Checkout : Popup
|
||||
{
|
||||
public string Branch
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Checkout(Repository repo, string branch) {
|
||||
public Checkout(Repository repo, string branch)
|
||||
{
|
||||
_repo = repo;
|
||||
Branch = branch;
|
||||
View = new Views.Checkout() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Checkout '{Branch}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Checkout(_repo.FullPath).Branch(Branch, SetProgressDescription);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo;
|
||||
private readonly Repository _repo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,42 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CherryPick : Popup {
|
||||
public Models.Commit Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CherryPick : Popup
|
||||
{
|
||||
public Models.Commit Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool AutoCommit {
|
||||
public bool AutoCommit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public CherryPick(Repository repo, Models.Commit target) {
|
||||
public CherryPick(Repository repo, Models.Commit target)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = target;
|
||||
AutoCommit = true;
|
||||
View = new Views.CherryPick() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Cherry-Pick commit '{Target.SHA}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.CherryPick(_repo.FullPath, Target.SHA, !AutoCommit).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,27 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Cleanup : Popup {
|
||||
public Cleanup(Repository repo) {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Cleanup : Popup
|
||||
{
|
||||
public Cleanup(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
View = new Views.Cleanup() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Cleanup (GC & prune) ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
new Commands.GC(_repo.FullPath, SetProgressDescription).Exec();
|
||||
|
||||
var lfs = new Commands.LFS(_repo.FullPath);
|
||||
if (lfs.IsEnabled()) {
|
||||
if (lfs.IsEnabled())
|
||||
{
|
||||
SetProgressDescription("Run LFS prune ...");
|
||||
lfs.Prune(SetProgressDescription);
|
||||
}
|
||||
|
@ -25,6 +31,6 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,28 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class ClearStashes : Popup {
|
||||
public ClearStashes(Repository repo) {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class ClearStashes : Popup
|
||||
{
|
||||
public ClearStashes(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
View = new Views.ClearStashes() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Clear all stashes...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
new Commands.Stash(_repo.FullPath).Clear();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,92 +3,114 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Clone : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Clone : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Remote URL is required")]
|
||||
[CustomValidation(typeof(Clone), nameof(ValidateRemote))]
|
||||
public string Remote {
|
||||
public string Remote
|
||||
{
|
||||
get => _remote;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _remote, value, true)) UseSSH = Models.Remote.IsSSH(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseSSH {
|
||||
public bool UseSSH
|
||||
{
|
||||
get => _useSSH;
|
||||
set => SetProperty(ref _useSSH, value);
|
||||
}
|
||||
|
||||
public string SSHKey {
|
||||
public string SSHKey
|
||||
{
|
||||
get => _sshKey;
|
||||
set => SetProperty(ref _sshKey, value);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Parent folder is required")]
|
||||
[CustomValidation(typeof(Clone), nameof(ValidateParentFolder))]
|
||||
public string ParentFolder {
|
||||
public string ParentFolder
|
||||
{
|
||||
get => _parentFolder;
|
||||
set => SetProperty(ref _parentFolder, value, true);
|
||||
}
|
||||
|
||||
public string Local {
|
||||
public string Local
|
||||
{
|
||||
get => _local;
|
||||
set => SetProperty(ref _local, value);
|
||||
}
|
||||
|
||||
public string ExtraArgs {
|
||||
public string ExtraArgs
|
||||
{
|
||||
get => _extraArgs;
|
||||
set => SetProperty(ref _extraArgs, value);
|
||||
}
|
||||
|
||||
public Clone(Launcher launcher, LauncherPage page) {
|
||||
public Clone(Launcher launcher, LauncherPage page)
|
||||
{
|
||||
_launcher = launcher;
|
||||
_page = page;
|
||||
|
||||
View = new Views.Clone() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemote(string remote, ValidationContext _) {
|
||||
public static ValidationResult ValidateRemote(string remote, ValidationContext _)
|
||||
{
|
||||
if (!Models.Remote.IsValidURL(remote)) return new ValidationResult("Invalid remote repository URL format");
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateParentFolder(string folder, ValidationContext _) {
|
||||
public static ValidationResult ValidateParentFolder(string folder, ValidationContext _)
|
||||
{
|
||||
if (!Directory.Exists(folder)) return new ValidationResult("Given path can NOT be found");
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
ProgressDescription = "Clone ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var cmd = new Commands.Clone(HostPageId, _parentFolder, _remote, _local, _useSSH ? _sshKey : "", _extraArgs, SetProgressDescription);
|
||||
if (!cmd.Exec()) return false;
|
||||
|
||||
var path = _parentFolder;
|
||||
if (!string.IsNullOrEmpty(_local)) {
|
||||
if (!string.IsNullOrEmpty(_local))
|
||||
{
|
||||
path = Path.GetFullPath(Path.Combine(path, _local));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = Path.GetFileName(_remote);
|
||||
if (name.EndsWith(".git")) name = name.Substring(0, name.Length - 4);
|
||||
path = Path.GetFullPath(Path.Combine(path, name));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(path)) {
|
||||
CallUIThread(() => {
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
CallUIThread(() =>
|
||||
{
|
||||
App.RaiseException(HostPageId, $"Folder '{path}' can NOT be found");
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_useSSH && !string.IsNullOrEmpty(_sshKey)) {
|
||||
if (_useSSH && !string.IsNullOrEmpty(_sshKey))
|
||||
{
|
||||
var config = new Commands.Config(path);
|
||||
config.Set("remote.origin.sshkey", _sshKey);
|
||||
}
|
||||
|
||||
CallUIThread(() => {
|
||||
CallUIThread(() =>
|
||||
{
|
||||
var repo = Preference.AddRepository(path, Path.Combine(path, ".git"));
|
||||
var node = new RepositoryNode() {
|
||||
var node = new RepositoryNode()
|
||||
{
|
||||
Id = repo.FullPath,
|
||||
Name = Path.GetFileName(repo.FullPath),
|
||||
Bookmark = 0,
|
||||
|
@ -103,8 +125,8 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Launcher _launcher = null;
|
||||
private LauncherPage _page = null;
|
||||
private readonly Launcher _launcher = null;
|
||||
private readonly LauncherPage _page = null;
|
||||
private string _remote = string.Empty;
|
||||
private bool _useSSH = false;
|
||||
private string _sshKey = string.Empty;
|
||||
|
@ -112,4 +134,4 @@ namespace SourceGit.ViewModels {
|
|||
private string _local = string.Empty;
|
||||
private string _extraArgs = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +1,71 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CommitDetail : ObservableObject {
|
||||
public DiffContext DiffContext {
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CommitDetail : ObservableObject
|
||||
{
|
||||
public DiffContext DiffContext
|
||||
{
|
||||
get => _diffContext;
|
||||
private set => SetProperty(ref _diffContext, value);
|
||||
}
|
||||
|
||||
public int ActivePageIndex {
|
||||
public int ActivePageIndex
|
||||
{
|
||||
get => _activePageIndex;
|
||||
set => SetProperty(ref _activePageIndex, value);
|
||||
}
|
||||
|
||||
public Models.Commit Commit {
|
||||
public Models.Commit Commit
|
||||
{
|
||||
get => _commit;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _commit, value)) Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Models.Change> Changes {
|
||||
public List<Models.Change> Changes
|
||||
{
|
||||
get => _changes;
|
||||
set => SetProperty(ref _changes, value);
|
||||
}
|
||||
|
||||
public List<Models.Change> VisibleChanges {
|
||||
public List<Models.Change> VisibleChanges
|
||||
{
|
||||
get => _visibleChanges;
|
||||
set => SetProperty(ref _visibleChanges, value);
|
||||
}
|
||||
|
||||
public List<FileTreeNode> ChangeTree {
|
||||
public List<FileTreeNode> ChangeTree
|
||||
{
|
||||
get => _changeTree;
|
||||
set => SetProperty(ref _changeTree, value);
|
||||
}
|
||||
|
||||
public Models.Change SelectedChange {
|
||||
public Models.Change SelectedChange
|
||||
{
|
||||
get => _selectedChange;
|
||||
set {
|
||||
if (SetProperty(ref _selectedChange, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedChange, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedChangeNode = null;
|
||||
DiffContext = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedChangeNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value));
|
||||
}
|
||||
|
@ -56,63 +73,84 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public FileTreeNode SelectedChangeNode {
|
||||
public FileTreeNode SelectedChangeNode
|
||||
{
|
||||
get => _selectedChangeNode;
|
||||
set {
|
||||
if (SetProperty(ref _selectedChangeNode, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedChangeNode, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedChange = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedChange = value.Backend as Models.Change;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SearchChangeFilter {
|
||||
public string SearchChangeFilter
|
||||
{
|
||||
get => _searchChangeFilter;
|
||||
set {
|
||||
if (SetProperty(ref _searchChangeFilter, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchChangeFilter, value))
|
||||
{
|
||||
RefreshVisibleChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<FileTreeNode> RevisionFilesTree {
|
||||
public List<FileTreeNode> RevisionFilesTree
|
||||
{
|
||||
get => _revisionFilesTree;
|
||||
set => SetProperty(ref _revisionFilesTree, value);
|
||||
}
|
||||
|
||||
public FileTreeNode SelectedRevisionFileNode {
|
||||
public FileTreeNode SelectedRevisionFileNode
|
||||
{
|
||||
get => _selectedRevisionFileNode;
|
||||
set {
|
||||
if (SetProperty(ref _selectedRevisionFileNode, value) && value != null && !value.IsFolder) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedRevisionFileNode, value) && value != null && !value.IsFolder)
|
||||
{
|
||||
RefreshViewRevisionFile(value.Backend as Models.Object);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewRevisionFileContent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SearchFileFilter {
|
||||
public string SearchFileFilter
|
||||
{
|
||||
get => _searchFileFilter;
|
||||
set {
|
||||
if (SetProperty(ref _searchFileFilter, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchFileFilter, value))
|
||||
{
|
||||
RefreshVisibleFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object ViewRevisionFileContent {
|
||||
public object ViewRevisionFileContent
|
||||
{
|
||||
get => _viewRevisionFileContent;
|
||||
set => SetProperty(ref _viewRevisionFileContent, value);
|
||||
}
|
||||
|
||||
public CommitDetail(string repo) {
|
||||
public CommitDetail(string repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
public void Cleanup()
|
||||
{
|
||||
_repo = null;
|
||||
_commit = null;
|
||||
if (_changes != null) _changes.Clear();
|
||||
|
@ -130,27 +168,33 @@ namespace SourceGit.ViewModels {
|
|||
_cancelToken = null;
|
||||
}
|
||||
|
||||
public void NavigateTo(string commitSHA) {
|
||||
public void NavigateTo(string commitSHA)
|
||||
{
|
||||
var repo = Preference.FindRepository(_repo);
|
||||
if (repo != null) repo.NavigateToCommit(commitSHA);
|
||||
}
|
||||
|
||||
public void ClearSearchChangeFilter() {
|
||||
public void ClearSearchChangeFilter()
|
||||
{
|
||||
SearchChangeFilter = string.Empty;
|
||||
}
|
||||
|
||||
public void ClearSearchFileFilter() {
|
||||
public void ClearSearchFileFilter()
|
||||
{
|
||||
SearchFileFilter = string.Empty;
|
||||
}
|
||||
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change) {
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted) {
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) => {
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) };
|
||||
window.Show();
|
||||
ev.Handled = true;
|
||||
|
@ -159,7 +203,8 @@ namespace SourceGit.ViewModels {
|
|||
var blame = new MenuItem();
|
||||
blame.Header = App.Text("Blame");
|
||||
blame.Icon = App.CreateMenuIcon("Icons.Blame");
|
||||
blame.Click += (o, ev) => {
|
||||
blame.Click += (o, ev) =>
|
||||
{
|
||||
var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) };
|
||||
window.Show();
|
||||
ev.Handled = true;
|
||||
|
@ -170,7 +215,8 @@ namespace SourceGit.ViewModels {
|
|||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) => {
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -183,7 +229,8 @@ namespace SourceGit.ViewModels {
|
|||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (_, ev) => {
|
||||
copyPath.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -192,11 +239,13 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateRevisionFileContextMenu(Models.Object file) {
|
||||
public ContextMenu CreateRevisionFileContextMenu(Models.Object file)
|
||||
{
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, ev) => {
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path) };
|
||||
window.Show();
|
||||
ev.Handled = true;
|
||||
|
@ -205,7 +254,8 @@ namespace SourceGit.ViewModels {
|
|||
var blame = new MenuItem();
|
||||
blame.Header = App.Text("Blame");
|
||||
blame.Icon = App.CreateMenuIcon("Icons.Blame");
|
||||
blame.Click += (o, ev) => {
|
||||
blame.Click += (o, ev) =>
|
||||
{
|
||||
var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) };
|
||||
window.Show();
|
||||
ev.Handled = true;
|
||||
|
@ -215,7 +265,8 @@ namespace SourceGit.ViewModels {
|
|||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Click += (_, ev) => {
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, file.Type == Models.ObjectType.Blob);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -224,13 +275,15 @@ namespace SourceGit.ViewModels {
|
|||
saveAs.Header = App.Text("SaveAs");
|
||||
saveAs.Icon = App.CreateMenuIcon("Icons.Save");
|
||||
saveAs.IsEnabled = file.Type == Models.ObjectType.Blob;
|
||||
saveAs.Click += async (_, ev) => {
|
||||
saveAs.Click += async (_, ev) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
|
||||
if (selected.Count == 1) {
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path));
|
||||
Commands.SaveRevisionFile.Run(_repo, _commit.SHA, file.Path, saveTo);
|
||||
}
|
||||
|
@ -241,7 +294,8 @@ namespace SourceGit.ViewModels {
|
|||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (_, ev) => {
|
||||
copyPath.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(file.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -255,7 +309,8 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
private void Refresh() {
|
||||
private void Refresh()
|
||||
{
|
||||
_changes = null;
|
||||
VisibleChanges = null;
|
||||
SelectedChange = null;
|
||||
|
@ -268,59 +323,75 @@ namespace SourceGit.ViewModels {
|
|||
var cmdChanges = new Commands.QueryCommitChanges(_repo, _commit.SHA) { Cancel = _cancelToken };
|
||||
var cmdRevisionFiles = new Commands.QueryRevisionObjects(_repo, _commit.SHA) { Cancel = _cancelToken };
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var changes = cmdChanges.Result();
|
||||
if (cmdChanges.Cancel.Requested) return;
|
||||
|
||||
var visible = changes;
|
||||
if (!string.IsNullOrWhiteSpace(_searchChangeFilter)) {
|
||||
if (!string.IsNullOrWhiteSpace(_searchChangeFilter))
|
||||
{
|
||||
visible = new List<Models.Change>();
|
||||
foreach (var c in changes) {
|
||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tree = FileTreeNode.Build(visible);
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Changes = changes;
|
||||
VisibleChanges = visible;
|
||||
ChangeTree = tree;
|
||||
});
|
||||
});
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var files = cmdRevisionFiles.Result();
|
||||
if (cmdRevisionFiles.Cancel.Requested) return;
|
||||
|
||||
var visible = files;
|
||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter)) {
|
||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
||||
{
|
||||
visible = new List<Models.Object>();
|
||||
foreach (var f in files) {
|
||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var f in files)
|
||||
{
|
||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tree = FileTreeNode.Build(visible);
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
_revisionFiles = files;
|
||||
RevisionFilesTree = tree;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void RefreshVisibleChanges() {
|
||||
private void RefreshVisibleChanges()
|
||||
{
|
||||
if (_changes == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(_searchChangeFilter)) {
|
||||
if (string.IsNullOrEmpty(_searchChangeFilter))
|
||||
{
|
||||
VisibleChanges = _changes;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var visible = new List<Models.Change>();
|
||||
foreach (var c in _changes) {
|
||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var c in _changes)
|
||||
{
|
||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(c);
|
||||
}
|
||||
}
|
||||
|
@ -331,14 +402,18 @@ namespace SourceGit.ViewModels {
|
|||
ChangeTree = FileTreeNode.Build(_visibleChanges);
|
||||
}
|
||||
|
||||
private void RefreshVisibleFiles() {
|
||||
private void RefreshVisibleFiles()
|
||||
{
|
||||
if (_revisionFiles == null) return;
|
||||
|
||||
var visible = _revisionFiles;
|
||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter)) {
|
||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
||||
{
|
||||
visible = new List<Models.Object>();
|
||||
foreach (var f in _revisionFiles) {
|
||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var f in _revisionFiles)
|
||||
{
|
||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(f);
|
||||
}
|
||||
}
|
||||
|
@ -347,52 +422,66 @@ namespace SourceGit.ViewModels {
|
|||
RevisionFilesTree = FileTreeNode.Build(visible);
|
||||
}
|
||||
|
||||
private void RefreshViewRevisionFile(Models.Object file) {
|
||||
switch (file.Type) {
|
||||
case Models.ObjectType.Blob:
|
||||
Task.Run(() => {
|
||||
var isBinary = new Commands.IsBinary(_repo, _commit.SHA, file.Path).Result();
|
||||
if (isBinary) {
|
||||
var size = new Commands.QueryFileSize(_repo, file.Path, _commit.SHA).Result();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
ViewRevisionFileContent = new Models.RevisionBinaryFile() { Size = size };
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var content = new Commands.QueryFileContent(_repo, _commit.SHA, file.Path).Result();
|
||||
if (content.StartsWith("version https://git-lfs.github.com/spec/", StringComparison.Ordinal)) {
|
||||
var obj = new Models.RevisionLFSObject() { Object = new Models.LFSObject() };
|
||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (lines.Length == 3) {
|
||||
foreach (var line in lines) {
|
||||
if (line.StartsWith("oid sha256:", StringComparison.Ordinal)) {
|
||||
obj.Object.Oid = line.Substring(11);
|
||||
} else if (line.StartsWith("size ", StringComparison.Ordinal)) {
|
||||
obj.Object.Size = long.Parse(line.Substring(5));
|
||||
}
|
||||
}
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
ViewRevisionFileContent = obj;
|
||||
private void RefreshViewRevisionFile(Models.Object file)
|
||||
{
|
||||
switch (file.Type)
|
||||
{
|
||||
case Models.ObjectType.Blob:
|
||||
Task.Run(() =>
|
||||
{
|
||||
var isBinary = new Commands.IsBinary(_repo, _commit.SHA, file.Path).Result();
|
||||
if (isBinary)
|
||||
{
|
||||
var size = new Commands.QueryFileSize(_repo, file.Path, _commit.SHA).Result();
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
ViewRevisionFileContent = new Models.RevisionBinaryFile() { Size = size };
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
ViewRevisionFileContent = new Models.RevisionTextFile() {
|
||||
FileName = file.Path,
|
||||
Content = content
|
||||
};
|
||||
var content = new Commands.QueryFileContent(_repo, _commit.SHA, file.Path).Result();
|
||||
if (content.StartsWith("version https://git-lfs.github.com/spec/", StringComparison.Ordinal))
|
||||
{
|
||||
var obj = new Models.RevisionLFSObject() { Object = new Models.LFSObject() };
|
||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (lines.Length == 3)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.StartsWith("oid sha256:", StringComparison.Ordinal))
|
||||
{
|
||||
obj.Object.Oid = line.Substring(11);
|
||||
}
|
||||
else if (line.StartsWith("size ", StringComparison.Ordinal))
|
||||
{
|
||||
obj.Object.Size = long.Parse(line.Substring(5));
|
||||
}
|
||||
}
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
ViewRevisionFileContent = obj;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
ViewRevisionFileContent = new Models.RevisionTextFile()
|
||||
{
|
||||
FileName = file.Path,
|
||||
Content = content
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
case Models.ObjectType.Commit:
|
||||
ViewRevisionFileContent = new Models.RevisionSubmodule() { SHA = file.SHA };
|
||||
break;
|
||||
default:
|
||||
ViewRevisionFileContent = null;
|
||||
break;
|
||||
break;
|
||||
case Models.ObjectType.Commit:
|
||||
ViewRevisionFileContent = new Models.RevisionSubmodule() { SHA = file.SHA };
|
||||
break;
|
||||
default:
|
||||
ViewRevisionFileContent = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,4 +502,4 @@ namespace SourceGit.ViewModels {
|
|||
private object _viewRevisionFileContent = null;
|
||||
private Commands.Command.CancelToken _cancelToken = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +1,44 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CreateBranch : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CreateBranch : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Branch name is required!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(CreateBranch), nameof(ValidateBranchName))]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
public object BasedOn {
|
||||
public object BasedOn
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool CheckoutAfterCreated {
|
||||
public bool CheckoutAfterCreated
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = true;
|
||||
|
||||
public bool AutoStash {
|
||||
public bool AutoStash
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = true;
|
||||
|
||||
public CreateBranch(Repository repo, Models.Branch branch) {
|
||||
public CreateBranch(Repository repo, Models.Branch branch)
|
||||
{
|
||||
_repo = repo;
|
||||
_baseOnRevision = branch.FullName;
|
||||
|
||||
if (!branch.IsLocal && repo.Branches.Find(x => x.IsLocal && x.Name == branch.Name) == null) {
|
||||
if (!branch.IsLocal && repo.Branches.Find(x => x.IsLocal && x.Name == branch.Name) == null)
|
||||
{
|
||||
Name = branch.Name;
|
||||
}
|
||||
|
||||
|
@ -38,7 +46,8 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.CreateBranch() { DataContext = this };
|
||||
}
|
||||
|
||||
public CreateBranch(Repository repo, Models.Commit commit) {
|
||||
public CreateBranch(Repository repo, Models.Commit commit)
|
||||
{
|
||||
_repo = repo;
|
||||
_baseOnRevision = commit.SHA;
|
||||
|
||||
|
@ -46,7 +55,8 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.CreateBranch() { DataContext = this };
|
||||
}
|
||||
|
||||
public CreateBranch(Repository repo, Models.Tag tag) {
|
||||
public CreateBranch(Repository repo, Models.Tag tag)
|
||||
{
|
||||
_repo = repo;
|
||||
_baseOnRevision = tag.SHA;
|
||||
|
||||
|
@ -54,11 +64,13 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.CreateBranch() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) {
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx)
|
||||
{
|
||||
var creator = ctx.ObjectInstance as CreateBranch;
|
||||
if (creator == null) return new ValidationResult("Missing runtime context to create branch!");
|
||||
|
||||
foreach (var b in creator._repo.Branches) {
|
||||
foreach (var b in creator._repo.Branches)
|
||||
{
|
||||
var test = b.IsLocal ? b.Name : $"{b.Remote}/{b.Name}";
|
||||
if (test == name) return new ValidationResult("A branch with same name already exists!");
|
||||
}
|
||||
|
@ -66,27 +78,36 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
return Task.Run(() => {
|
||||
if (CheckoutAfterCreated) {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (CheckoutAfterCreated)
|
||||
{
|
||||
bool needPopStash = false;
|
||||
if (_repo.WorkingCopyChangesCount > 0) {
|
||||
if (AutoStash) {
|
||||
if (_repo.WorkingCopyChangesCount > 0)
|
||||
{
|
||||
if (AutoStash)
|
||||
{
|
||||
SetProgressDescription("Adding untracked changes...");
|
||||
var succ = new Commands.Add(_repo.FullPath).Exec();
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
SetProgressDescription("Stash local changes");
|
||||
succ = new Commands.Stash(_repo.FullPath).Push("CREATE_BRANCH_AUTO_STASH");
|
||||
}
|
||||
|
||||
if (!succ) {
|
||||
if (!succ)
|
||||
{
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return false;
|
||||
}
|
||||
|
||||
needPopStash = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetProgressDescription("Discard local changes...");
|
||||
Commands.Discard.All(_repo.FullPath);
|
||||
}
|
||||
|
@ -95,11 +116,14 @@ namespace SourceGit.ViewModels {
|
|||
SetProgressDescription($"Create new branch '{_name}'");
|
||||
new Commands.Checkout(_repo.FullPath).Branch(_name, _baseOnRevision, SetProgressDescription);
|
||||
|
||||
if (needPopStash) {
|
||||
if (needPopStash)
|
||||
{
|
||||
SetProgressDescription("Re-apply local changes...");
|
||||
new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.Branch.Create(_repo.FullPath, _name, _baseOnRevision);
|
||||
}
|
||||
|
||||
|
@ -108,8 +132,8 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _name = null;
|
||||
private string _baseOnRevision = null;
|
||||
private readonly string _baseOnRevision = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,21 +2,27 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CreateGroup : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CreateGroup : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Group name is required!")]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
public CreateGroup(RepositoryNode parent) {
|
||||
public CreateGroup(RepositoryNode parent)
|
||||
{
|
||||
_parent = parent;
|
||||
View = new Views.CreateGroup() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
Preference.AddNode(new RepositoryNode() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
Preference.AddNode(new RepositoryNode()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = _name,
|
||||
IsRepository = false,
|
||||
|
@ -26,7 +32,7 @@ namespace SourceGit.ViewModels {
|
|||
return null;
|
||||
}
|
||||
|
||||
private RepositoryNode _parent = null;
|
||||
private readonly RepositoryNode _parent = null;
|
||||
private string _name = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,27 +3,33 @@ using System.ComponentModel.DataAnnotations;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CreateTag : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CreateTag : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Tag name is required!")]
|
||||
[RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad tag name format!")]
|
||||
[CustomValidation(typeof(CreateTag), nameof(ValidateTagName))]
|
||||
public string TagName {
|
||||
public string TagName
|
||||
{
|
||||
get => _tagName;
|
||||
set => SetProperty(ref _tagName, value, true);
|
||||
}
|
||||
|
||||
public string Message {
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public object BasedOn {
|
||||
public object BasedOn
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public CreateTag(Repository repo, Models.Branch branch) {
|
||||
public CreateTag(Repository repo, Models.Branch branch)
|
||||
{
|
||||
_repo = repo;
|
||||
_basedOn = branch.Head;
|
||||
|
||||
|
@ -31,7 +37,8 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.CreateTag() { DataContext = this };
|
||||
}
|
||||
|
||||
public CreateTag(Repository repo, Models.Commit commit) {
|
||||
public CreateTag(Repository repo, Models.Commit commit)
|
||||
{
|
||||
_repo = repo;
|
||||
_basedOn = commit.SHA;
|
||||
|
||||
|
@ -39,28 +46,32 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.CreateTag() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateTagName(string name, ValidationContext ctx) {
|
||||
public static ValidationResult ValidateTagName(string name, ValidationContext ctx)
|
||||
{
|
||||
var creator = ctx.ObjectInstance as CreateTag;
|
||||
if (creator != null) {
|
||||
if (creator != null)
|
||||
{
|
||||
var found = creator._repo.Tags.Find(x => x.Name == name);
|
||||
if (found != null) return new ValidationResult("A tag with same name already exists!");
|
||||
}
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Create tag...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
Commands.Tag.Add(_repo.FullPath, TagName, _basedOn, Message);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _tagName = string.Empty;
|
||||
private string _basedOn = string.Empty;
|
||||
private readonly string _basedOn = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DeleteBranch : Popup {
|
||||
public Models.Branch Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DeleteBranch : Popup
|
||||
{
|
||||
public Models.Branch Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DeleteBranch(Repository repo, Models.Branch branch) {
|
||||
public DeleteBranch(Repository repo, Models.Branch branch)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = branch;
|
||||
View = new Views.DeleteBranch() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Deleting branch...";
|
||||
|
||||
return Task.Run(() => {
|
||||
if (Target.IsLocal) {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (Target.IsLocal)
|
||||
{
|
||||
Commands.Branch.Delete(_repo.FullPath, Target.Name);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
new Commands.Push(_repo.FullPath, Target.Remote, Target.Name).Exec();
|
||||
}
|
||||
|
||||
|
@ -29,6 +38,6 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,32 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DeleteRemote : Popup {
|
||||
public Models.Remote Remote {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DeleteRemote : Popup
|
||||
{
|
||||
public Models.Remote Remote
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DeleteRemote(Repository repo, Models.Remote remote) {
|
||||
public DeleteRemote(Repository repo, Models.Remote remote)
|
||||
{
|
||||
_repo = repo;
|
||||
Remote = remote;
|
||||
View = new Views.DeleteRemote() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Deleting remote ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Remote(_repo.FullPath).Delete(Remote.Name);
|
||||
CallUIThread(() => {
|
||||
CallUIThread(() =>
|
||||
{
|
||||
_repo.MarkBranchesDirtyManually();
|
||||
_repo.SetWatcherEnabled(true);
|
||||
});
|
||||
|
@ -27,6 +34,6 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,27 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DeleteRepositoryNode : Popup {
|
||||
public RepositoryNode Node {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DeleteRepositoryNode : Popup
|
||||
{
|
||||
public RepositoryNode Node
|
||||
{
|
||||
get => _node;
|
||||
set => SetProperty(ref _node, value);
|
||||
}
|
||||
|
||||
public DeleteRepositoryNode(RepositoryNode node) {
|
||||
public DeleteRepositoryNode(RepositoryNode node)
|
||||
{
|
||||
_node = node;
|
||||
View = new Views.DeleteRepositoryNode() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
Preference.RemoveNode(_node);
|
||||
return null;
|
||||
}
|
||||
|
||||
private RepositoryNode _node = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +1,36 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DeleteSubmodule : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DeleteSubmodule : Popup
|
||||
{
|
||||
|
||||
public string Submodule {
|
||||
public string Submodule
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DeleteSubmodule(Repository repo, string submodule) {
|
||||
public DeleteSubmodule(Repository repo, string submodule)
|
||||
{
|
||||
_repo = repo;
|
||||
Submodule = submodule;
|
||||
View = new Views.DeleteSubmodule() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Deleting submodule ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Submodule(_repo.FullPath).Delete(Submodule);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +1,36 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DeleteTag : Popup {
|
||||
public Models.Tag Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DeleteTag : Popup
|
||||
{
|
||||
public Models.Tag Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool ShouldPushToRemote {
|
||||
public bool ShouldPushToRemote
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DeleteTag(Repository repo, Models.Tag tag) {
|
||||
public DeleteTag(Repository repo, Models.Tag tag)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = tag;
|
||||
ShouldPushToRemote = true;
|
||||
View = new Views.DeleteTag() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Deleting tag '{Target.Name}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var remotes = ShouldPushToRemote ? _repo.Remotes : null;
|
||||
var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
|
@ -31,6 +38,6 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +1,77 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DiffContext : ObservableObject {
|
||||
public string RepositoryPath {
|
||||
using Avalonia;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DiffContext : ObservableObject
|
||||
{
|
||||
public string RepositoryPath
|
||||
{
|
||||
get => _repo;
|
||||
}
|
||||
|
||||
public Models.Change WorkingCopyChange {
|
||||
public Models.Change WorkingCopyChange
|
||||
{
|
||||
get => _option.WorkingCopyChange;
|
||||
}
|
||||
|
||||
public bool IsUnstaged {
|
||||
public bool IsUnstaged
|
||||
{
|
||||
get => _option.IsUnstaged;
|
||||
}
|
||||
|
||||
public string FilePath {
|
||||
public string FilePath
|
||||
{
|
||||
get => _option.Path;
|
||||
}
|
||||
|
||||
public bool IsOrgFilePathVisible {
|
||||
public bool IsOrgFilePathVisible
|
||||
{
|
||||
get => !string.IsNullOrWhiteSpace(_option.OrgPath) && _option.OrgPath != "/dev/null";
|
||||
}
|
||||
|
||||
public string OrgFilePath {
|
||||
public string OrgFilePath
|
||||
{
|
||||
get => _option.OrgPath;
|
||||
}
|
||||
|
||||
public bool IsLoading {
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
private set => SetProperty(ref _isLoading, value);
|
||||
}
|
||||
|
||||
public bool IsNoChange {
|
||||
public bool IsNoChange
|
||||
{
|
||||
get => _isNoChange;
|
||||
private set => SetProperty(ref _isNoChange, value);
|
||||
}
|
||||
|
||||
public bool IsTextDiff {
|
||||
public bool IsTextDiff
|
||||
{
|
||||
get => _isTextDiff;
|
||||
private set => SetProperty(ref _isTextDiff, value);
|
||||
}
|
||||
|
||||
public object Content {
|
||||
public object Content
|
||||
{
|
||||
get => _content;
|
||||
private set => SetProperty(ref _content, value);
|
||||
}
|
||||
|
||||
public Vector SyncScrollOffset {
|
||||
public Vector SyncScrollOffset
|
||||
{
|
||||
get => _syncScrollOffset;
|
||||
set => SetProperty(ref _syncScrollOffset, value);
|
||||
}
|
||||
|
||||
public DiffContext(string repo, Models.DiffOption option) {
|
||||
public DiffContext(string repo, Models.DiffOption option)
|
||||
{
|
||||
_repo = repo;
|
||||
_option = option;
|
||||
|
||||
|
@ -63,33 +79,46 @@ namespace SourceGit.ViewModels {
|
|||
OnPropertyChanged(nameof(IsOrgFilePathVisible));
|
||||
OnPropertyChanged(nameof(OrgFilePath));
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var latest = new Commands.Diff(repo, option).Result();
|
||||
var binaryDiff = null as Models.BinaryDiff;
|
||||
|
||||
if (latest.IsBinary) {
|
||||
if (latest.IsBinary)
|
||||
{
|
||||
binaryDiff = new Models.BinaryDiff();
|
||||
|
||||
var oldPath = string.IsNullOrEmpty(_option.OrgPath) ? _option.Path : _option.OrgPath;
|
||||
if (option.Revisions.Count == 2) {
|
||||
if (option.Revisions.Count == 2)
|
||||
{
|
||||
binaryDiff.OldSize = new Commands.QueryFileSize(repo, oldPath, option.Revisions[0]).Result();
|
||||
binaryDiff.NewSize = new Commands.QueryFileSize(repo, _option.Path, option.Revisions[1]).Result();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
binaryDiff.OldSize = new Commands.QueryFileSize(repo, oldPath, "HEAD").Result();
|
||||
binaryDiff.NewSize = new FileInfo(Path.Combine(repo, _option.Path)).Length;
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() => {
|
||||
if (latest.IsBinary) {
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (latest.IsBinary)
|
||||
{
|
||||
Content = binaryDiff;
|
||||
} else if (latest.IsLFS) {
|
||||
}
|
||||
else if (latest.IsLFS)
|
||||
{
|
||||
Content = latest.LFSDiff;
|
||||
} else if (latest.TextDiff != null) {
|
||||
}
|
||||
else if (latest.TextDiff != null)
|
||||
{
|
||||
latest.TextDiff.File = _option.Path;
|
||||
Content = latest.TextDiff;
|
||||
IsTextDiff = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
IsTextDiff = false;
|
||||
IsNoChange = true;
|
||||
}
|
||||
|
@ -99,12 +128,14 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
public async void OpenExternalMergeTool() {
|
||||
public async void OpenExternalMergeTool()
|
||||
{
|
||||
var type = Preference.Instance.ExternalMergeToolType;
|
||||
var exec = Preference.Instance.ExternalMergeToolPath;
|
||||
|
||||
var tool = Models.ExternalMergeTools.Supported.Find(x => x.Type == type);
|
||||
if (tool == null || !File.Exists(exec)) {
|
||||
if (tool == null || !File.Exists(exec))
|
||||
{
|
||||
App.RaiseException(_repo, "Invalid merge tool in preference setting!");
|
||||
return;
|
||||
}
|
||||
|
@ -113,12 +144,12 @@ namespace SourceGit.ViewModels {
|
|||
await Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, exec, args, _option));
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private Models.DiffOption _option = null;
|
||||
private readonly string _repo = string.Empty;
|
||||
private readonly Models.DiffOption _option = null;
|
||||
private bool _isLoading = true;
|
||||
private bool _isNoChange = false;
|
||||
private bool _isTextDiff = false;
|
||||
private object _content = null;
|
||||
private Vector _syncScrollOffset = Vector.Zero;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +1,68 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DiscardModeAll { }
|
||||
public class DiscardModeSingle { public string File { get; set; } }
|
||||
public class DiscardModeMulti { public int Count { get; set; } }
|
||||
|
||||
public class Discard : Popup {
|
||||
public class Discard : Popup
|
||||
{
|
||||
|
||||
public object Mode {
|
||||
public object Mode
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Discard(Repository repo) {
|
||||
public Discard(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
Mode = new DiscardModeAll();
|
||||
View = new Views.Discard { DataContext = this };
|
||||
}
|
||||
|
||||
public Discard(Repository repo, List<Models.Change> changes, bool isUnstaged) {
|
||||
public Discard(Repository repo, List<Models.Change> changes, bool isUnstaged)
|
||||
{
|
||||
_repo = repo;
|
||||
_changes = changes;
|
||||
_isUnstaged = isUnstaged;
|
||||
|
||||
if (_changes == null) {
|
||||
if (_changes == null)
|
||||
{
|
||||
Mode = new DiscardModeAll();
|
||||
} else if (_changes.Count == 1) {
|
||||
}
|
||||
else if (_changes.Count == 1)
|
||||
{
|
||||
Mode = new DiscardModeSingle() { File = _changes[0].Path };
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Mode = new DiscardModeMulti() { Count = _changes.Count };
|
||||
}
|
||||
|
||||
View = new Views.Discard() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = _changes == null ? "Discard all local changes ..." : $"Discard total {_changes.Count} changes ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
if (_changes == null) {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (_changes == null)
|
||||
{
|
||||
Commands.Discard.All(_repo.FullPath);
|
||||
} else if (_isUnstaged) {
|
||||
}
|
||||
else if (_isUnstaged)
|
||||
{
|
||||
Commands.Discard.ChangesInWorkTree(_repo.FullPath, _changes);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Commands.Discard.ChangesInStaged(_repo.FullPath, _changes);
|
||||
}
|
||||
|
||||
|
@ -54,8 +71,8 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private List<Models.Change> _changes = null;
|
||||
private bool _isUnstaged = true;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly List<Models.Change> _changes = null;
|
||||
private readonly bool _isUnstaged = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,29 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class DropStash : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class DropStash : Popup
|
||||
{
|
||||
public Models.Stash Stash { get; private set; }
|
||||
|
||||
public DropStash(string repo, Models.Stash stash) {
|
||||
public DropStash(string repo, Models.Stash stash)
|
||||
{
|
||||
_repo = repo;
|
||||
Stash = stash;
|
||||
View = new Views.DropStash() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
ProgressDescription = $"Dropping stash: {Stash.Name}";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
new Commands.Stash(_repo).Drop(Stash.Name);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private string _repo;
|
||||
private readonly string _repo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +1,64 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class EditRemote : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class EditRemote : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Remote name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-\.]+$", ErrorMessage = "Bad remote name format!!!")]
|
||||
[CustomValidation(typeof(EditRemote), nameof(ValidateRemoteName))]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Remote URL is required!!!")]
|
||||
[CustomValidation(typeof(EditRemote), nameof(ValidateRemoteURL))]
|
||||
public string Url {
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _url, value, true)) UseSSH = Models.Remote.IsSSH(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseSSH {
|
||||
public bool UseSSH
|
||||
{
|
||||
get => _useSSH;
|
||||
set => SetProperty(ref _useSSH, value);
|
||||
}
|
||||
|
||||
public string SSHKey {
|
||||
public string SSHKey
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public EditRemote(Repository repo, Models.Remote remote) {
|
||||
public EditRemote(Repository repo, Models.Remote remote)
|
||||
{
|
||||
_repo = repo;
|
||||
_remote = remote;
|
||||
_name = remote.Name;
|
||||
_url = remote.URL;
|
||||
_useSSH = Models.Remote.IsSSH(remote.URL);
|
||||
|
||||
if (_useSSH) {
|
||||
if (_useSSH)
|
||||
{
|
||||
SSHKey = new Commands.Config(repo.FullPath).Get($"remote.{remote.Name}.sshkey");
|
||||
}
|
||||
|
||||
View = new Views.EditRemote() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is EditRemote edit) {
|
||||
foreach (var remote in edit._repo.Remotes) {
|
||||
public static ValidationResult ValidateRemoteName(string name, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is EditRemote edit)
|
||||
{
|
||||
foreach (var remote in edit._repo.Remotes)
|
||||
{
|
||||
if (remote != edit._remote && name == remote.Name) new ValidationResult("A remote with given name already exists!!!");
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +66,14 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is EditRemote edit) {
|
||||
public static ValidationResult ValidateRemoteURL(string url, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is EditRemote edit)
|
||||
{
|
||||
if (!Models.Remote.IsValidURL(url)) return new ValidationResult("Bad remote URL format!!!");
|
||||
|
||||
foreach (var remote in edit._repo.Remotes) {
|
||||
foreach (var remote in edit._repo.Remotes)
|
||||
{
|
||||
if (remote != edit._remote && url == remote.URL) new ValidationResult("A remote with the same url already exists!!!");
|
||||
}
|
||||
}
|
||||
|
@ -66,22 +81,27 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Editing remote '{_remote.Name}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
if (_remote.Name != _name) {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (_remote.Name != _name)
|
||||
{
|
||||
var succ = new Commands.Remote(_repo.FullPath).Rename(_remote.Name, _name);
|
||||
if (succ) _remote.Name = _name;
|
||||
}
|
||||
|
||||
if (_remote.URL != _url) {
|
||||
if (_remote.URL != _url)
|
||||
{
|
||||
var succ = new Commands.Remote(_repo.FullPath).SetURL(_name, _url);
|
||||
if (succ) _remote.URL = _url;
|
||||
}
|
||||
|
||||
if (_useSSH) {
|
||||
if (_useSSH)
|
||||
{
|
||||
SetProgressDescription("Post processing ...");
|
||||
new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", SSHKey);
|
||||
}
|
||||
|
@ -91,10 +111,10 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private Models.Remote _remote = null;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.Remote _remote = null;
|
||||
private string _name = string.Empty;
|
||||
private string _url = string.Empty;
|
||||
private bool _useSSH = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,35 +2,43 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class EditRepositoryNode : Popup {
|
||||
public RepositoryNode Node {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class EditRepositoryNode : Popup
|
||||
{
|
||||
public RepositoryNode Node
|
||||
{
|
||||
get => _node;
|
||||
set => SetProperty(ref _node, value);
|
||||
}
|
||||
|
||||
public string Id {
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
set => SetProperty(ref _id, value);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Name is required!")]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
public int Bookmark {
|
||||
public int Bookmark
|
||||
{
|
||||
get => _bookmark;
|
||||
set => SetProperty(ref _bookmark, value);
|
||||
}
|
||||
|
||||
public bool IsRepository {
|
||||
public bool IsRepository
|
||||
{
|
||||
get => _isRepository;
|
||||
set => SetProperty(ref _isRepository, value);
|
||||
}
|
||||
|
||||
public EditRepositoryNode(RepositoryNode node) {
|
||||
public EditRepositoryNode(RepositoryNode node)
|
||||
{
|
||||
_node = node;
|
||||
_id = node.Id;
|
||||
_name = node.Name;
|
||||
|
@ -40,16 +48,17 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.EditRepositoryNode() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_node.Name = _name;
|
||||
_node.Bookmark = _bookmark;
|
||||
return null;
|
||||
}
|
||||
|
||||
private RepositoryNode _node = null;
|
||||
private RepositoryNode _node = null;
|
||||
private string _id = string.Empty;
|
||||
private string _name = string.Empty;
|
||||
private bool _isRepository = false;
|
||||
private int _bookmark = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,42 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class FastForwardWithoutCheckout : Popup {
|
||||
public Models.Branch Local {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class FastForwardWithoutCheckout : Popup
|
||||
{
|
||||
public Models.Branch Local
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Branch To {
|
||||
public Models.Branch To
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public FastForwardWithoutCheckout(Repository repo, Models.Branch local, Models.Branch upstream) {
|
||||
public FastForwardWithoutCheckout(Repository repo, Models.Branch local, Models.Branch upstream)
|
||||
{
|
||||
_repo = repo;
|
||||
Local = local;
|
||||
To = upstream;
|
||||
View = new Views.FastForwardWithoutCheckout() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Fast-Forward ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
new Commands.Fetch(_repo.FullPath, To.Remote, Local.Name, To.Name, SetProgressDescription).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Fetch : Popup {
|
||||
public List<Models.Remote> Remotes {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Fetch : Popup
|
||||
{
|
||||
public List<Models.Remote> Remotes
|
||||
{
|
||||
get => _repo.Remotes;
|
||||
}
|
||||
|
||||
public bool FetchAllRemotes {
|
||||
public bool FetchAllRemotes
|
||||
{
|
||||
get => _fetchAllRemotes;
|
||||
set => SetProperty(ref _fetchAllRemotes, value);
|
||||
}
|
||||
|
||||
public Models.Remote SelectedRemote {
|
||||
public Models.Remote SelectedRemote
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool Prune {
|
||||
public bool Prune
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Fetch(Repository repo, Models.Remote preferedRemote = null) {
|
||||
public Fetch(Repository repo, Models.Remote preferedRemote = null)
|
||||
{
|
||||
_repo = repo;
|
||||
_fetchAllRemotes = preferedRemote == null;
|
||||
SelectedRemote = preferedRemote != null ? preferedRemote : _repo.Remotes[0];
|
||||
|
@ -30,15 +37,21 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Fetch() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
return Task.Run(() => {
|
||||
if (FetchAllRemotes) {
|
||||
foreach (var remote in _repo.Remotes) {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (FetchAllRemotes)
|
||||
{
|
||||
foreach (var remote in _repo.Remotes)
|
||||
{
|
||||
SetProgressDescription($"Fetching remote: {remote.Name}");
|
||||
new Commands.Fetch(_repo.FullPath, remote.Name, Prune, SetProgressDescription).Exec();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SetProgressDescription($"Fetching remote: {SelectedRemote.Name}");
|
||||
new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, Prune, SetProgressDescription).Exec();
|
||||
}
|
||||
|
@ -48,7 +61,7 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private bool _fetchAllRemotes = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +1,66 @@
|
|||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class FileHistories : ObservableObject {
|
||||
public string File {
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class FileHistories : ObservableObject
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get => _file;
|
||||
}
|
||||
|
||||
public bool IsLoading {
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
private set => SetProperty(ref _isLoading, value);
|
||||
}
|
||||
|
||||
public List<Models.Commit> Commits {
|
||||
public List<Models.Commit> Commits
|
||||
{
|
||||
get => _commits;
|
||||
set => SetProperty(ref _commits, value);
|
||||
}
|
||||
|
||||
public Models.Commit SelectedCommit {
|
||||
public Models.Commit SelectedCommit
|
||||
{
|
||||
get => _selectedCommit;
|
||||
set {
|
||||
if (SetProperty(ref _selectedCommit, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedCommit, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
DiffContext = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(value, _file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiffContext DiffContext {
|
||||
public DiffContext DiffContext
|
||||
{
|
||||
get => _diffContext;
|
||||
set => SetProperty(ref _diffContext, value);
|
||||
}
|
||||
|
||||
public FileHistories(string repo, string file) {
|
||||
public FileHistories(string repo, string file)
|
||||
{
|
||||
_repo = repo;
|
||||
_file = file;
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var commits = new Commands.QueryCommits(_repo, $"-n 10000 -- \"{file}\"").Result();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
IsLoading = false;
|
||||
Commits = commits;
|
||||
if (commits.Count > 0) SelectedCommit = commits[0];
|
||||
|
@ -51,16 +68,17 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
public void NavigateToCommit(string commitSHA) {
|
||||
public void NavigateToCommit(string commitSHA)
|
||||
{
|
||||
var repo = Preference.FindRepository(_repo);
|
||||
if (repo != null) repo.NavigateToCommit(commitSHA);
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private string _file = string.Empty;
|
||||
private readonly string _repo = string.Empty;
|
||||
private readonly string _file = string.Empty;
|
||||
private bool _isLoading = true;
|
||||
private List<Models.Commit> _commits = null;
|
||||
private Models.Commit _selectedCommit = null;
|
||||
private DiffContext _diffContext = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +1,58 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class FileTreeNode : ObservableObject {
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class FileTreeNode : ObservableObject
|
||||
{
|
||||
public string FullPath { get; set; } = string.Empty;
|
||||
public bool IsFolder { get; set; } = false;
|
||||
public object Backend { get; set; } = null;
|
||||
public List<FileTreeNode> Children { get; set; } = new List<FileTreeNode>();
|
||||
|
||||
public bool IsExpanded {
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetProperty(ref _isExpanded, value);
|
||||
}
|
||||
|
||||
public static List<FileTreeNode> Build(List<Models.Change> changes) {
|
||||
public static List<FileTreeNode> Build(List<Models.Change> changes)
|
||||
{
|
||||
var nodes = new List<FileTreeNode>();
|
||||
var folders = new Dictionary<string, FileTreeNode>();
|
||||
var expanded = changes.Count <= 50;
|
||||
|
||||
foreach (var c in changes) {
|
||||
foreach (var c in changes)
|
||||
{
|
||||
var sepIdx = c.Path.IndexOf('/', StringComparison.Ordinal);
|
||||
if (sepIdx == -1) {
|
||||
nodes.Add(new FileTreeNode() {
|
||||
if (sepIdx == -1)
|
||||
{
|
||||
nodes.Add(new FileTreeNode()
|
||||
{
|
||||
FullPath = c.Path,
|
||||
Backend = c,
|
||||
IsFolder = false,
|
||||
IsExpanded = false
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
FileTreeNode lastFolder = null;
|
||||
var start = 0;
|
||||
|
||||
while (sepIdx != -1) {
|
||||
while (sepIdx != -1)
|
||||
{
|
||||
var folder = c.Path.Substring(0, sepIdx);
|
||||
if (folders.ContainsKey(folder)) {
|
||||
if (folders.ContainsKey(folder))
|
||||
{
|
||||
lastFolder = folders[folder];
|
||||
} else if (lastFolder == null) {
|
||||
lastFolder = new FileTreeNode() {
|
||||
}
|
||||
else if (lastFolder == null)
|
||||
{
|
||||
lastFolder = new FileTreeNode()
|
||||
{
|
||||
FullPath = folder,
|
||||
Backend = null,
|
||||
IsFolder = true,
|
||||
|
@ -45,8 +60,11 @@ namespace SourceGit.ViewModels {
|
|||
};
|
||||
nodes.Add(lastFolder);
|
||||
folders.Add(folder, lastFolder);
|
||||
} else {
|
||||
var cur = new FileTreeNode() {
|
||||
}
|
||||
else
|
||||
{
|
||||
var cur = new FileTreeNode()
|
||||
{
|
||||
FullPath = folder,
|
||||
Backend = null,
|
||||
IsFolder = true,
|
||||
|
@ -61,7 +79,8 @@ namespace SourceGit.ViewModels {
|
|||
sepIdx = c.Path.IndexOf('/', start);
|
||||
}
|
||||
|
||||
lastFolder.Children.Add(new FileTreeNode() {
|
||||
lastFolder.Children.Add(new FileTreeNode()
|
||||
{
|
||||
FullPath = c.Path,
|
||||
Backend = c,
|
||||
IsFolder = false,
|
||||
|
@ -75,30 +94,41 @@ namespace SourceGit.ViewModels {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
public static List<FileTreeNode> Build(List<Models.Object> files) {
|
||||
public static List<FileTreeNode> Build(List<Models.Object> files)
|
||||
{
|
||||
var nodes = new List<FileTreeNode>();
|
||||
var folders = new Dictionary<string, FileTreeNode>();
|
||||
var expanded = files.Count <= 50;
|
||||
|
||||
foreach (var f in files) {
|
||||
foreach (var f in files)
|
||||
{
|
||||
var sepIdx = f.Path.IndexOf('/', StringComparison.Ordinal);
|
||||
if (sepIdx == -1) {
|
||||
nodes.Add(new FileTreeNode() {
|
||||
if (sepIdx == -1)
|
||||
{
|
||||
nodes.Add(new FileTreeNode()
|
||||
{
|
||||
FullPath = f.Path,
|
||||
Backend = f,
|
||||
IsFolder = false,
|
||||
IsExpanded = false
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
FileTreeNode lastFolder = null;
|
||||
var start = 0;
|
||||
|
||||
while (sepIdx != -1) {
|
||||
while (sepIdx != -1)
|
||||
{
|
||||
var folder = f.Path.Substring(0, sepIdx);
|
||||
if (folders.ContainsKey(folder)) {
|
||||
if (folders.ContainsKey(folder))
|
||||
{
|
||||
lastFolder = folders[folder];
|
||||
} else if (lastFolder == null) {
|
||||
lastFolder = new FileTreeNode() {
|
||||
}
|
||||
else if (lastFolder == null)
|
||||
{
|
||||
lastFolder = new FileTreeNode()
|
||||
{
|
||||
FullPath = folder,
|
||||
Backend = null,
|
||||
IsFolder = true,
|
||||
|
@ -106,8 +136,11 @@ namespace SourceGit.ViewModels {
|
|||
};
|
||||
nodes.Add(lastFolder);
|
||||
folders.Add(folder, lastFolder);
|
||||
} else {
|
||||
var cur = new FileTreeNode() {
|
||||
}
|
||||
else
|
||||
{
|
||||
var cur = new FileTreeNode()
|
||||
{
|
||||
FullPath = folder,
|
||||
Backend = null,
|
||||
IsFolder = true,
|
||||
|
@ -122,7 +155,8 @@ namespace SourceGit.ViewModels {
|
|||
sepIdx = f.Path.IndexOf('/', start);
|
||||
}
|
||||
|
||||
lastFolder.Children.Add(new FileTreeNode() {
|
||||
lastFolder.Children.Add(new FileTreeNode()
|
||||
{
|
||||
FullPath = f.Path,
|
||||
Backend = f,
|
||||
IsFolder = false,
|
||||
|
@ -136,13 +170,17 @@ namespace SourceGit.ViewModels {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
public static FileTreeNode SelectByPath(List<FileTreeNode> nodes, string path) {
|
||||
foreach (var node in nodes) {
|
||||
public static FileTreeNode SelectByPath(List<FileTreeNode> nodes, string path)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (node.FullPath == path) return node;
|
||||
|
||||
if (node.IsFolder && path.StartsWith(node.FullPath + "/", StringComparison.Ordinal)) {
|
||||
|
||||
if (node.IsFolder && path.StartsWith(node.FullPath + "/", StringComparison.Ordinal))
|
||||
{
|
||||
var foundInChildren = SelectByPath(node.Children, path);
|
||||
if (foundInChildren != null) {
|
||||
if (foundInChildren != null)
|
||||
{
|
||||
node.IsExpanded = true;
|
||||
}
|
||||
return foundInChildren;
|
||||
|
@ -152,20 +190,26 @@ namespace SourceGit.ViewModels {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static void Sort(List<FileTreeNode> nodes) {
|
||||
nodes.Sort((l, r) => {
|
||||
if (l.IsFolder == r.IsFolder) {
|
||||
private static void Sort(List<FileTreeNode> nodes)
|
||||
{
|
||||
nodes.Sort((l, r) =>
|
||||
{
|
||||
if (l.IsFolder == r.IsFolder)
|
||||
{
|
||||
return l.FullPath.CompareTo(r.FullPath);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return l.IsFolder ? -1 : 1;
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var node in nodes) {
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (node.Children.Count > 1) Sort(node.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isExpanded = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +1,45 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class GitFlowFinish : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class GitFlowFinish : Popup
|
||||
{
|
||||
public Models.Branch Branch => _branch;
|
||||
public bool IsFeature => _type == Models.GitFlowBranchType.Feature;
|
||||
public bool IsRelease => _type == Models.GitFlowBranchType.Release;
|
||||
public bool IsHotfix => _type == Models.GitFlowBranchType.Hotfix;
|
||||
|
||||
public bool KeepBranch {
|
||||
public bool KeepBranch
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = false;
|
||||
|
||||
public GitFlowFinish(Repository repo, Models.Branch branch, Models.GitFlowBranchType type) {
|
||||
public GitFlowFinish(Repository repo, Models.Branch branch, Models.GitFlowBranchType type)
|
||||
{
|
||||
_repo = repo;
|
||||
_branch = branch;
|
||||
_type = type;
|
||||
View = new Views.GitFlowFinish() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var branch = _branch.Name;
|
||||
switch (_type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
branch = branch.Substring(_repo.GitFlow.Feature.Length);
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
branch = branch.Substring(_repo.GitFlow.Release.Length);
|
||||
break;
|
||||
default:
|
||||
branch = branch.Substring(_repo.GitFlow.Hotfix.Length);
|
||||
break;
|
||||
switch (_type)
|
||||
{
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
branch = branch.Substring(_repo.GitFlow.Feature.Length);
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
branch = branch.Substring(_repo.GitFlow.Release.Length);
|
||||
break;
|
||||
default:
|
||||
branch = branch.Substring(_repo.GitFlow.Hotfix.Length);
|
||||
break;
|
||||
}
|
||||
|
||||
var succ = new Commands.GitFlow(_repo.FullPath).Finish(_type, branch, KeepBranch);
|
||||
|
@ -41,8 +48,8 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private Models.Branch _branch = null;
|
||||
private Models.GitFlowBranchType _type = Models.GitFlowBranchType.None;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.Branch _branch = null;
|
||||
private readonly Models.GitFlowBranchType _type = Models.GitFlowBranchType.None;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class GitFlowStart : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class GitFlowStart : Popup
|
||||
{
|
||||
[Required(ErrorMessage = "Name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(GitFlowStart), nameof(ValidateBranchName))]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
public string Prefix {
|
||||
public string Prefix
|
||||
{
|
||||
get => _prefix;
|
||||
}
|
||||
|
||||
|
@ -19,29 +23,34 @@ namespace SourceGit.ViewModels {
|
|||
public bool IsRelease => _type == Models.GitFlowBranchType.Release;
|
||||
public bool IsHotfix => _type == Models.GitFlowBranchType.Hotfix;
|
||||
|
||||
public GitFlowStart(Repository repo, Models.GitFlowBranchType type) {
|
||||
public GitFlowStart(Repository repo, Models.GitFlowBranchType type)
|
||||
{
|
||||
_repo = repo;
|
||||
_type = type;
|
||||
|
||||
switch (type) {
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
_prefix = repo.GitFlow.Feature;
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
_prefix = repo.GitFlow.Release;
|
||||
break;
|
||||
default:
|
||||
_prefix = repo.GitFlow.Hotfix;
|
||||
break;
|
||||
switch (type)
|
||||
{
|
||||
case Models.GitFlowBranchType.Feature:
|
||||
_prefix = repo.GitFlow.Feature;
|
||||
break;
|
||||
case Models.GitFlowBranchType.Release:
|
||||
_prefix = repo.GitFlow.Release;
|
||||
break;
|
||||
default:
|
||||
_prefix = repo.GitFlow.Hotfix;
|
||||
break;
|
||||
}
|
||||
|
||||
View = new Views.GitFlowStart() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is GitFlowStart starter) {
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is GitFlowStart starter)
|
||||
{
|
||||
var check = $"{starter._prefix}{name}";
|
||||
foreach (var b in starter._repo.Branches) {
|
||||
foreach (var b in starter._repo.Branches)
|
||||
{
|
||||
var test = b.IsLocal ? b.Name : $"{b.Remote}/{b.Name}";
|
||||
if (test == check) return new ValidationResult("A branch with same name already exists!");
|
||||
}
|
||||
|
@ -50,18 +59,20 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.GitFlow(_repo.FullPath).Start(_type, _name);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private Models.GitFlowBranchType _type = Models.GitFlowBranchType.Feature;
|
||||
private string _prefix = string.Empty;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.GitFlowBranchType _type = Models.GitFlowBranchType.Feature;
|
||||
private readonly string _prefix = string.Empty;
|
||||
private string _name = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,47 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class CountSelectedCommits {
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class CountSelectedCommits
|
||||
{
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public class Histories : ObservableObject {
|
||||
public bool IsLoading {
|
||||
public class Histories : ObservableObject
|
||||
{
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
set => SetProperty(ref _isLoading, value);
|
||||
}
|
||||
|
||||
public double DataGridRowHeight {
|
||||
public double DataGridRowHeight
|
||||
{
|
||||
get => _dataGridRowHeight;
|
||||
}
|
||||
|
||||
public List<Models.Commit> Commits {
|
||||
public List<Models.Commit> Commits
|
||||
{
|
||||
get => _commits;
|
||||
set {
|
||||
if (SetProperty(ref _commits, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _commits, value))
|
||||
{
|
||||
Graph = null;
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var graph = Models.CommitGraph.Parse(value, DataGridRowHeight, 8);
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Graph = graph;
|
||||
});
|
||||
});
|
||||
|
@ -37,49 +49,62 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public Models.CommitGraph Graph {
|
||||
public Models.CommitGraph Graph
|
||||
{
|
||||
get => _graph;
|
||||
set => SetProperty(ref _graph, value);
|
||||
}
|
||||
|
||||
public Models.Commit AutoSelectedCommit {
|
||||
public Models.Commit AutoSelectedCommit
|
||||
{
|
||||
get => _autoSelectedCommit;
|
||||
private set => SetProperty(ref _autoSelectedCommit, value);
|
||||
}
|
||||
|
||||
public object DetailContext {
|
||||
public object DetailContext
|
||||
{
|
||||
get => _detailContext;
|
||||
private set => SetProperty(ref _detailContext, value);
|
||||
}
|
||||
|
||||
public Histories(Repository repo) {
|
||||
public Histories(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
public void Cleanup()
|
||||
{
|
||||
Commits = new List<Models.Commit>();
|
||||
|
||||
_repo = null;
|
||||
_graph = null;
|
||||
_autoSelectedCommit = null;
|
||||
|
||||
if (_detailContext is CommitDetail cd) {
|
||||
if (_detailContext is CommitDetail cd)
|
||||
{
|
||||
cd.Cleanup();
|
||||
} else if (_detailContext is RevisionCompare rc) {
|
||||
}
|
||||
else if (_detailContext is RevisionCompare rc)
|
||||
{
|
||||
rc.Cleanup();
|
||||
}
|
||||
|
||||
_detailContext = null;
|
||||
}
|
||||
|
||||
public void NavigateTo(string commitSHA) {
|
||||
public void NavigateTo(string commitSHA)
|
||||
{
|
||||
var commit = _commits.Find(x => x.SHA.StartsWith(commitSHA, StringComparison.Ordinal));
|
||||
if (commit != null) {
|
||||
if (commit != null)
|
||||
{
|
||||
AutoSelectedCommit = commit;
|
||||
|
||||
if (_detailContext is CommitDetail detail) {
|
||||
if (_detailContext is CommitDetail detail)
|
||||
{
|
||||
detail.Commit = commit;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var commitDetail = new CommitDetail(_repo.FullPath);
|
||||
commitDetail.Commit = commit;
|
||||
DetailContext = commitDetail;
|
||||
|
@ -87,30 +112,42 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public void Select(IList commits) {
|
||||
if (commits.Count == 0) {
|
||||
public void Select(IList commits)
|
||||
{
|
||||
if (commits.Count == 0)
|
||||
{
|
||||
DetailContext = null;
|
||||
} else if (commits.Count == 1) {
|
||||
}
|
||||
else if (commits.Count == 1)
|
||||
{
|
||||
var commit = commits[0] as Models.Commit;
|
||||
AutoSelectedCommit = commit;
|
||||
|
||||
if (_detailContext is CommitDetail detail) {
|
||||
if (_detailContext is CommitDetail detail)
|
||||
{
|
||||
detail.Commit = commit;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var commitDetail = new CommitDetail(_repo.FullPath);
|
||||
commitDetail.Commit = commit;
|
||||
DetailContext = commitDetail;
|
||||
}
|
||||
} else if (commits.Count == 2) {
|
||||
}
|
||||
else if (commits.Count == 2)
|
||||
{
|
||||
var end = commits[0] as Models.Commit;
|
||||
var start = commits[1] as Models.Commit;
|
||||
DetailContext = new RevisionCompare(_repo.FullPath, start, end);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DetailContext = new CountSelectedCommits() { Count = commits.Count };
|
||||
}
|
||||
}
|
||||
|
||||
public ContextMenu MakeContextMenu() {
|
||||
public ContextMenu MakeContextMenu()
|
||||
{
|
||||
var detail = _detailContext as CommitDetail;
|
||||
if (detail == null) return null;
|
||||
|
||||
|
@ -121,17 +158,26 @@ namespace SourceGit.ViewModels {
|
|||
var menu = new ContextMenu();
|
||||
var tags = new List<Models.Tag>();
|
||||
|
||||
if (commit.HasDecorators) {
|
||||
foreach (var d in commit.Decorators) {
|
||||
if (d.Type == Models.DecoratorType.CurrentBranchHead) {
|
||||
if (commit.HasDecorators)
|
||||
{
|
||||
foreach (var d in commit.Decorators)
|
||||
{
|
||||
if (d.Type == Models.DecoratorType.CurrentBranchHead)
|
||||
{
|
||||
FillCurrentBranchMenu(menu, current);
|
||||
} else if (d.Type == Models.DecoratorType.LocalBranchHead) {
|
||||
}
|
||||
else if (d.Type == Models.DecoratorType.LocalBranchHead)
|
||||
{
|
||||
var b = _repo.Branches.Find(x => x.IsLocal && d.Name == x.Name);
|
||||
FillOtherLocalBranchMenu(menu, b, current, commit.IsMerged);
|
||||
} else if (d.Type == Models.DecoratorType.RemoteBranchHead) {
|
||||
}
|
||||
else if (d.Type == Models.DecoratorType.RemoteBranchHead)
|
||||
{
|
||||
var b = _repo.Branches.Find(x => !x.IsLocal && d.Name == $"{x.Remote}/{x.Name}");
|
||||
FillRemoteBranchMenu(menu, b, current, commit.IsMerged);
|
||||
} else if (d.Type == Models.DecoratorType.Tag) {
|
||||
}
|
||||
else if (d.Type == Models.DecoratorType.Tag)
|
||||
{
|
||||
var t = _repo.Tags.Find(x => x.Name == d.Name);
|
||||
if (t != null) tags.Add(t);
|
||||
}
|
||||
|
@ -140,25 +186,31 @@ namespace SourceGit.ViewModels {
|
|||
if (menu.Items.Count > 0) menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
}
|
||||
|
||||
if (tags.Count > 0) {
|
||||
if (tags.Count > 0)
|
||||
{
|
||||
foreach (var tag in tags) FillTagMenu(menu, tag);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
}
|
||||
|
||||
if (current.Head != commit.SHA) {
|
||||
if (current.Head != commit.SHA)
|
||||
{
|
||||
var reset = new MenuItem();
|
||||
reset.Header = new Views.NameHighlightedTextBlock("CommitCM.Reset", current.Name);
|
||||
reset.Icon = App.CreateMenuIcon("Icons.Reset");
|
||||
reset.Click += (o, e) => {
|
||||
reset.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reset(_repo, current, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(reset);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var reword = new MenuItem();
|
||||
reword.Header = App.Text("CommitCM.Reword");
|
||||
reword.Icon = App.CreateMenuIcon("Icons.Edit");
|
||||
reword.Click += (o, e) => {
|
||||
reword.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reword(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -168,22 +220,26 @@ namespace SourceGit.ViewModels {
|
|||
squash.Header = App.Text("CommitCM.Squash");
|
||||
squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent");
|
||||
squash.IsEnabled = commit.Parents.Count == 1;
|
||||
squash.Click += (o, e) => {
|
||||
if (commit.Parents.Count == 1) {
|
||||
squash.Click += (o, e) =>
|
||||
{
|
||||
if (commit.Parents.Count == 1)
|
||||
{
|
||||
var parent = _commits.Find(x => x.SHA == commit.Parents[0]);
|
||||
if (parent != null && PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Squash(_repo, commit, parent));
|
||||
}
|
||||
|
||||
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(squash);
|
||||
}
|
||||
|
||||
if (!commit.IsMerged) {
|
||||
if (!commit.IsMerged)
|
||||
{
|
||||
var rebase = new MenuItem();
|
||||
rebase.Header = new Views.NameHighlightedTextBlock("CommitCM.Rebase", current.Name);
|
||||
rebase.Icon = App.CreateMenuIcon("Icons.Rebase");
|
||||
rebase.Click += (o, e) => {
|
||||
rebase.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(_repo, current, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -192,16 +248,20 @@ namespace SourceGit.ViewModels {
|
|||
var cherryPick = new MenuItem();
|
||||
cherryPick.Header = App.Text("CommitCM.CherryPick");
|
||||
cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick");
|
||||
cherryPick.Click += (o, e) => {
|
||||
cherryPick.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CherryPick(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
menu.Items.Add(cherryPick);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var revert = new MenuItem();
|
||||
revert.Header = App.Text("CommitCM.Revert");
|
||||
revert.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
revert.Click += (o, e) => {
|
||||
revert.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Revert(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -213,7 +273,8 @@ namespace SourceGit.ViewModels {
|
|||
var createBranch = new MenuItem();
|
||||
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
|
||||
createBranch.Header = App.Text("CreateBranch");
|
||||
createBranch.Click += (o, e) => {
|
||||
createBranch.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -222,7 +283,8 @@ namespace SourceGit.ViewModels {
|
|||
var createTag = new MenuItem();
|
||||
createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add");
|
||||
createTag.Header = App.Text("CreateTag");
|
||||
createTag.Click += (o, e) => {
|
||||
createTag.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -232,13 +294,15 @@ namespace SourceGit.ViewModels {
|
|||
var saveToPatch = new MenuItem();
|
||||
saveToPatch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
saveToPatch.Header = App.Text("CommitCM.SaveAsPatch");
|
||||
saveToPatch.Click += async (_, e) => {
|
||||
saveToPatch.Click += async (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
var options = new FolderPickerOpenOptions() { AllowMultiple = false };
|
||||
var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options);
|
||||
if (selected.Count == 1) {
|
||||
if (selected.Count == 1)
|
||||
{
|
||||
var succ = new Commands.FormatPatch(_repo.FullPath, commit.SHA, selected[0].Path.LocalPath).Exec();
|
||||
if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
@ -250,7 +314,8 @@ namespace SourceGit.ViewModels {
|
|||
var archive = new MenuItem();
|
||||
archive.Icon = App.CreateMenuIcon("Icons.Archive");
|
||||
archive.Header = App.Text("Archive");
|
||||
archive.Click += (o, e) => {
|
||||
archive.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(_repo, commit));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -260,7 +325,8 @@ namespace SourceGit.ViewModels {
|
|||
var copySHA = new MenuItem();
|
||||
copySHA.Header = App.Text("CommitCM.CopySHA");
|
||||
copySHA.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copySHA.Click += (o, e) => {
|
||||
copySHA.Click += (o, e) =>
|
||||
{
|
||||
App.CopyText(commit.SHA);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -268,19 +334,22 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current) {
|
||||
private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current)
|
||||
{
|
||||
var submenu = new MenuItem();
|
||||
submenu.Icon = App.CreateMenuIcon("Icons.Branch");
|
||||
submenu.Header = current.Name;
|
||||
|
||||
if (!string.IsNullOrEmpty(current.Upstream)) {
|
||||
if (!string.IsNullOrEmpty(current.Upstream))
|
||||
{
|
||||
var upstream = current.Upstream.Substring(13);
|
||||
|
||||
var fastForward = new MenuItem();
|
||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||
fastForward.IsEnabled = !string.IsNullOrEmpty(current.UpstreamTrackStatus) && current.UpstreamTrackStatus.IndexOf('↑') < 0; ;
|
||||
fastForward.Click += (o, e) => {
|
||||
fastForward.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -289,7 +358,8 @@ namespace SourceGit.ViewModels {
|
|||
var pull = new MenuItem();
|
||||
pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream);
|
||||
pull.Icon = App.CreateMenuIcon("Icons.Pull");
|
||||
pull.Click += (o, e) => {
|
||||
pull.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(_repo, null));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -300,7 +370,8 @@ namespace SourceGit.ViewModels {
|
|||
push.Header = new Views.NameHighlightedTextBlock("BranchCM.Push", current.Name);
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = _repo.Remotes.Count > 0;
|
||||
push.Click += (o, e) => {
|
||||
push.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(_repo, current));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -308,11 +379,13 @@ namespace SourceGit.ViewModels {
|
|||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var type = _repo.GitFlow.GetBranchType(current.Name);
|
||||
if (type != Models.GitFlowBranchType.None) {
|
||||
if (type != Models.GitFlowBranchType.None)
|
||||
{
|
||||
var finish = new MenuItem();
|
||||
finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", current.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.Flow");
|
||||
finish.Click += (o, e) => {
|
||||
finish.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, current, type));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -323,7 +396,8 @@ namespace SourceGit.ViewModels {
|
|||
var rename = new MenuItem();
|
||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", current.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (o, e) => {
|
||||
rename.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, current));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -332,7 +406,8 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(submenu);
|
||||
}
|
||||
|
||||
private void FillOtherLocalBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged) {
|
||||
private void FillOtherLocalBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged)
|
||||
{
|
||||
var submenu = new MenuItem();
|
||||
submenu.Icon = App.CreateMenuIcon("Icons.Branch");
|
||||
submenu.Header = branch.Name;
|
||||
|
@ -340,7 +415,8 @@ namespace SourceGit.ViewModels {
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) => {
|
||||
checkout.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, branch.Name));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -350,7 +426,8 @@ namespace SourceGit.ViewModels {
|
|||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.IsEnabled = !merged;
|
||||
merge.Click += (o, e) => {
|
||||
merge.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -358,11 +435,13 @@ namespace SourceGit.ViewModels {
|
|||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
var type = _repo.GitFlow.GetBranchType(branch.Name);
|
||||
if (type != Models.GitFlowBranchType.None) {
|
||||
if (type != Models.GitFlowBranchType.None)
|
||||
{
|
||||
var finish = new MenuItem();
|
||||
finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name);
|
||||
finish.Icon = App.CreateMenuIcon("Icons.Flow");
|
||||
finish.Click += (o, e) => {
|
||||
finish.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, type));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -373,7 +452,8 @@ namespace SourceGit.ViewModels {
|
|||
var rename = new MenuItem();
|
||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name);
|
||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||
rename.Click += (o, e) => {
|
||||
rename.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, branch));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -382,7 +462,8 @@ namespace SourceGit.ViewModels {
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", branch.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) => {
|
||||
delete.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -391,7 +472,8 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(submenu);
|
||||
}
|
||||
|
||||
private void FillRemoteBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged) {
|
||||
private void FillRemoteBranchMenu(ContextMenu menu, Models.Branch branch, Models.Branch current, bool merged)
|
||||
{
|
||||
var name = $"{branch.Remote}/{branch.Name}";
|
||||
|
||||
var submenu = new MenuItem();
|
||||
|
@ -401,9 +483,12 @@ namespace SourceGit.ViewModels {
|
|||
var checkout = new MenuItem();
|
||||
checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", name);
|
||||
checkout.Icon = App.CreateMenuIcon("Icons.Check");
|
||||
checkout.Click += (o, e) => {
|
||||
foreach (var b in _repo.Branches) {
|
||||
if (b.IsLocal && b.Upstream == branch.FullName) {
|
||||
checkout.Click += (o, e) =>
|
||||
{
|
||||
foreach (var b in _repo.Branches)
|
||||
{
|
||||
if (b.IsLocal && b.Upstream == branch.FullName)
|
||||
{
|
||||
if (b.IsCurrent) return;
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Checkout(_repo, b.Name));
|
||||
return;
|
||||
|
@ -419,7 +504,8 @@ namespace SourceGit.ViewModels {
|
|||
merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name);
|
||||
merge.Icon = App.CreateMenuIcon("Icons.Merge");
|
||||
merge.IsEnabled = !merged;
|
||||
merge.Click += (o, e) => {
|
||||
merge.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, name, current.Name));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -430,7 +516,8 @@ namespace SourceGit.ViewModels {
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) => {
|
||||
delete.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -439,7 +526,8 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(submenu);
|
||||
}
|
||||
|
||||
private void FillTagMenu(ContextMenu menu, Models.Tag tag) {
|
||||
private void FillTagMenu(ContextMenu menu, Models.Tag tag)
|
||||
{
|
||||
var submenu = new MenuItem();
|
||||
submenu.Header = tag.Name;
|
||||
submenu.Icon = App.CreateMenuIcon("Icons.Tag");
|
||||
|
@ -449,7 +537,8 @@ namespace SourceGit.ViewModels {
|
|||
push.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name);
|
||||
push.Icon = App.CreateMenuIcon("Icons.Push");
|
||||
push.IsEnabled = _repo.Remotes.Count > 0;
|
||||
push.Click += (o, e) => {
|
||||
push.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(_repo, tag));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -458,7 +547,8 @@ namespace SourceGit.ViewModels {
|
|||
var delete = new MenuItem();
|
||||
delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name);
|
||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||
delete.Click += (o, e) => {
|
||||
delete.Click += (o, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(_repo, tag));
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -468,11 +558,11 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private double _dataGridRowHeight = 28;
|
||||
private readonly double _dataGridRowHeight = 28;
|
||||
private bool _isLoading = true;
|
||||
private List<Models.Commit> _commits = new List<Models.Commit>();
|
||||
private Models.CommitGraph _graph = null;
|
||||
private Models.Commit _autoSelectedCommit = null;
|
||||
private object _detailContext = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +1,38 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Init : Popup {
|
||||
public string TargetPath {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Init : Popup
|
||||
{
|
||||
public string TargetPath
|
||||
{
|
||||
get => _targetPath;
|
||||
set => SetProperty(ref _targetPath, value);
|
||||
}
|
||||
|
||||
public Init(string path) {
|
||||
public Init(string path)
|
||||
{
|
||||
TargetPath = path;
|
||||
View = new Views.Init() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
ProgressDescription = $"Initialize git repository at: '{_targetPath}'";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Init(HostPageId, _targetPath).Exec();
|
||||
if (!succ) return false;
|
||||
|
||||
var gitDir = Path.GetFullPath(Path.Combine(_targetPath, ".git"));
|
||||
|
||||
CallUIThread(() => {
|
||||
|
||||
CallUIThread(() =>
|
||||
{
|
||||
var repo = Preference.AddRepository(_targetPath, gitDir);
|
||||
var node = new RepositoryNode() {
|
||||
var node = new RepositoryNode()
|
||||
{
|
||||
Id = repo.FullPath,
|
||||
Name = Path.GetFileName(repo.FullPath),
|
||||
Bookmark = 0,
|
||||
|
@ -39,4 +47,4 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
private string _targetPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,16 +2,19 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public partial class InitGitFlow : Popup {
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public partial class InitGitFlow : Popup
|
||||
{
|
||||
|
||||
[GeneratedRegex(@"^[\w\-/\.]+$")]
|
||||
private static partial Regex TAG_PREFIX();
|
||||
|
||||
[Required(ErrorMessage = "Master branch name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(InitGitFlow), nameof(ValidateBaseBranch))]
|
||||
public string Master {
|
||||
public string Master
|
||||
{
|
||||
get => _master;
|
||||
set => SetProperty(ref _master, value, true);
|
||||
}
|
||||
|
@ -19,66 +22,79 @@ namespace SourceGit.ViewModels {
|
|||
[Required(ErrorMessage = "Develop branch name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(InitGitFlow), nameof(ValidateBaseBranch))]
|
||||
public string Develop {
|
||||
public string Develop
|
||||
{
|
||||
get => _develop;
|
||||
set => SetProperty(ref _develop, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Feature prefix is required!!!")]
|
||||
[RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad feature prefix format!")]
|
||||
public string FeturePrefix {
|
||||
public string FeturePrefix
|
||||
{
|
||||
get => _featurePrefix;
|
||||
set => SetProperty(ref _featurePrefix, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Release prefix is required!!!")]
|
||||
[RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad release prefix format!")]
|
||||
public string ReleasePrefix {
|
||||
public string ReleasePrefix
|
||||
{
|
||||
get => _releasePrefix;
|
||||
set => SetProperty(ref _releasePrefix, value, true);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Hotfix prefix is required!!!")]
|
||||
[RegularExpression(@"^[\w\-\.]+/$", ErrorMessage = "Bad hotfix prefix format!")]
|
||||
public string HotfixPrefix {
|
||||
public string HotfixPrefix
|
||||
{
|
||||
get => _hotfixPrefix;
|
||||
set => SetProperty(ref _hotfixPrefix, value, true);
|
||||
}
|
||||
|
||||
[CustomValidation(typeof(InitGitFlow), nameof(ValidateTagPrefix))]
|
||||
public string TagPrefix {
|
||||
public string TagPrefix
|
||||
{
|
||||
get => _tagPrefix;
|
||||
set => SetProperty(ref _tagPrefix, value, true);
|
||||
}
|
||||
|
||||
public InitGitFlow(Repository repo) {
|
||||
public InitGitFlow(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
View = new Views.InitGitFlow() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateBaseBranch(string _, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is InitGitFlow initializer) {
|
||||
public static ValidationResult ValidateBaseBranch(string _, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is InitGitFlow initializer)
|
||||
{
|
||||
if (initializer._master == initializer._develop) return new ValidationResult("Develop branch has the same name with master branch!");
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateTagPrefix(string tagPrefix, ValidationContext ctx) {
|
||||
if (!string.IsNullOrWhiteSpace(tagPrefix) && !TAG_PREFIX().IsMatch(tagPrefix)) {
|
||||
public static ValidationResult ValidateTagPrefix(string tagPrefix, ValidationContext ctx)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(tagPrefix) && !TAG_PREFIX().IsMatch(tagPrefix))
|
||||
{
|
||||
return new ValidationResult("Bad tag prefix format!");
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Init git-flow ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.GitFlow(_repo.FullPath).Init(_repo.Branches, _master, _develop, _featurePrefix, _releasePrefix, _hotfixPrefix, _tagPrefix);
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
_repo.GitFlow.Feature = _featurePrefix;
|
||||
_repo.GitFlow.Release = _releasePrefix;
|
||||
_repo.GitFlow.Hotfix = _hotfixPrefix;
|
||||
|
@ -89,7 +105,7 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _master = "master";
|
||||
private string _develop = "develop";
|
||||
private string _featurePrefix = "feature/";
|
||||
|
@ -97,4 +113,4 @@ namespace SourceGit.ViewModels {
|
|||
private string _hotfixPrefix = "hotfix/";
|
||||
private string _tagPrefix = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +1,41 @@
|
|||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Launcher : ObservableObject {
|
||||
public AvaloniaList<LauncherPage> Pages {
|
||||
using Avalonia.Collections;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Launcher : ObservableObject
|
||||
{
|
||||
public AvaloniaList<LauncherPage> Pages
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public LauncherPage ActivePage {
|
||||
public LauncherPage ActivePage
|
||||
{
|
||||
get => _activePage;
|
||||
set {
|
||||
if (SetProperty(ref _activePage, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _activePage, value))
|
||||
{
|
||||
PopupHost.Active = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Launcher() {
|
||||
public Launcher()
|
||||
{
|
||||
Pages = new AvaloniaList<LauncherPage>();
|
||||
AddNewTab();
|
||||
|
||||
if (Preference.Instance.RestoreTabs) {
|
||||
foreach (var id in Preference.Instance.OpenedTabs) {
|
||||
if (Preference.Instance.RestoreTabs)
|
||||
{
|
||||
foreach (var id in Preference.Instance.OpenedTabs)
|
||||
{
|
||||
var node = Preference.FindNode(id);
|
||||
if (node == null) continue;
|
||||
|
||||
|
@ -32,17 +43,21 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
|
||||
var lastActiveIdx = Preference.Instance.LastActiveTabIdx;
|
||||
if (lastActiveIdx >= 0 && lastActiveIdx < Pages.Count) {
|
||||
if (lastActiveIdx >= 0 && lastActiveIdx < Pages.Count)
|
||||
{
|
||||
ActivePage = Pages[lastActiveIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Quit() {
|
||||
public void Quit()
|
||||
{
|
||||
Preference.Instance.OpenedTabs.Clear();
|
||||
|
||||
if (Preference.Instance.RestoreTabs) {
|
||||
foreach (var page in Pages) {
|
||||
if (Preference.Instance.RestoreTabs)
|
||||
{
|
||||
foreach (var page in Pages)
|
||||
{
|
||||
if (page.Node.IsRepository) Preference.Instance.OpenedTabs.Add(page.Node.Id);
|
||||
}
|
||||
}
|
||||
|
@ -51,20 +66,23 @@ namespace SourceGit.ViewModels {
|
|||
Preference.Save();
|
||||
}
|
||||
|
||||
public void AddNewTab() {
|
||||
public void AddNewTab()
|
||||
{
|
||||
var page = new LauncherPage();
|
||||
Pages.Add(page);
|
||||
ActivePage = page;
|
||||
}
|
||||
|
||||
public void MoveTab(LauncherPage from, LauncherPage to) {
|
||||
public void MoveTab(LauncherPage from, LauncherPage to)
|
||||
{
|
||||
var fromIdx = Pages.IndexOf(from);
|
||||
var toIdx = Pages.IndexOf(to);
|
||||
Pages.Move(fromIdx, toIdx);
|
||||
ActivePage = from;
|
||||
}
|
||||
|
||||
public void GotoNextTab() {
|
||||
public void GotoNextTab()
|
||||
{
|
||||
if (Pages.Count == 1) return;
|
||||
|
||||
var activeIdx = Pages.IndexOf(_activePage);
|
||||
|
@ -72,8 +90,10 @@ namespace SourceGit.ViewModels {
|
|||
ActivePage = Pages[nextIdx];
|
||||
}
|
||||
|
||||
public void CloseTab(object param) {
|
||||
if (Pages.Count == 1) {
|
||||
public void CloseTab(object param)
|
||||
{
|
||||
if (Pages.Count == 1)
|
||||
{
|
||||
App.Quit();
|
||||
return;
|
||||
}
|
||||
|
@ -83,21 +103,29 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
var removeIdx = Pages.IndexOf(page);
|
||||
var activeIdx = Pages.IndexOf(_activePage);
|
||||
if (removeIdx == activeIdx) {
|
||||
if (removeIdx == Pages.Count - 1) {
|
||||
if (removeIdx == activeIdx)
|
||||
{
|
||||
if (removeIdx == Pages.Count - 1)
|
||||
{
|
||||
ActivePage = Pages[removeIdx - 1];
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ActivePage = Pages[removeIdx + 1];
|
||||
}
|
||||
|
||||
CloseRepositoryInTab(page);
|
||||
Pages.RemoveAt(removeIdx);
|
||||
OnPropertyChanged(nameof(Pages));
|
||||
} else if (removeIdx + 1 == activeIdx) {
|
||||
}
|
||||
else if (removeIdx + 1 == activeIdx)
|
||||
{
|
||||
CloseRepositoryInTab(page);
|
||||
Pages.RemoveAt(removeIdx);
|
||||
OnPropertyChanged(nameof(Pages));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseRepositoryInTab(page);
|
||||
Pages.RemoveAt(removeIdx);
|
||||
}
|
||||
|
@ -105,7 +133,8 @@ namespace SourceGit.ViewModels {
|
|||
GC.Collect();
|
||||
}
|
||||
|
||||
public void CloseOtherTabs(object param) {
|
||||
public void CloseOtherTabs(object param)
|
||||
{
|
||||
if (Pages.Count == 1) return;
|
||||
|
||||
var page = param as LauncherPage;
|
||||
|
@ -113,7 +142,8 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
ActivePage = page;
|
||||
|
||||
foreach (var one in Pages) {
|
||||
foreach (var one in Pages)
|
||||
{
|
||||
if (one.Node.Id != page.Node.Id) CloseRepositoryInTab(one);
|
||||
}
|
||||
|
||||
|
@ -123,17 +153,20 @@ namespace SourceGit.ViewModels {
|
|||
GC.Collect();
|
||||
}
|
||||
|
||||
public void CloseRightTabs(object param) {
|
||||
public void CloseRightTabs(object param)
|
||||
{
|
||||
LauncherPage page = param as LauncherPage;
|
||||
if (page == null) page = _activePage;
|
||||
|
||||
var endIdx = Pages.IndexOf(page);
|
||||
var activeIdx = Pages.IndexOf(_activePage);
|
||||
if (endIdx < activeIdx) {
|
||||
if (endIdx < activeIdx)
|
||||
{
|
||||
ActivePage = page;
|
||||
}
|
||||
|
||||
for (var i = Pages.Count - 1; i > endIdx; i--) {
|
||||
for (var i = Pages.Count - 1; i > endIdx; i--)
|
||||
{
|
||||
CloseRepositoryInTab(Pages[i]);
|
||||
Pages.Remove(Pages[i]);
|
||||
}
|
||||
|
@ -141,16 +174,20 @@ namespace SourceGit.ViewModels {
|
|||
GC.Collect();
|
||||
}
|
||||
|
||||
public void OpenRepositoryInTab(RepositoryNode node, LauncherPage page) {
|
||||
foreach (var one in Pages) {
|
||||
if (one.Node.Id == node.Id) {
|
||||
public void OpenRepositoryInTab(RepositoryNode node, LauncherPage page)
|
||||
{
|
||||
foreach (var one in Pages)
|
||||
{
|
||||
if (one.Node.Id == node.Id)
|
||||
{
|
||||
ActivePage = one;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var repo = Preference.FindRepository(node.Id);
|
||||
if (repo == null || !Path.Exists(repo.FullPath)) {
|
||||
if (repo == null || !Path.Exists(repo.FullPath))
|
||||
{
|
||||
var ctx = page == null ? ActivePage.Node.Id : page.Node.Id;
|
||||
App.RaiseException(ctx, "Repository does NOT exists any more. Please remove it.");
|
||||
return;
|
||||
|
@ -159,16 +196,22 @@ namespace SourceGit.ViewModels {
|
|||
repo.Open();
|
||||
Commands.AutoFetch.AddRepository(repo.FullPath);
|
||||
|
||||
if (page == null) {
|
||||
if (ActivePage == null || ActivePage.Node.IsRepository) {
|
||||
if (page == null)
|
||||
{
|
||||
if (ActivePage == null || ActivePage.Node.IsRepository)
|
||||
{
|
||||
page = new LauncherPage(node, repo);
|
||||
Pages.Add(page);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
page = ActivePage;
|
||||
page.Node = node;
|
||||
page.Data = repo;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Node = node;
|
||||
page.Data = repo;
|
||||
}
|
||||
|
@ -176,8 +219,10 @@ namespace SourceGit.ViewModels {
|
|||
ActivePage = page;
|
||||
}
|
||||
|
||||
private void CloseRepositoryInTab(LauncherPage page) {
|
||||
if (page.Data is Repository repo) {
|
||||
private void CloseRepositoryInTab(LauncherPage page)
|
||||
{
|
||||
if (page.Data is Repository repo)
|
||||
{
|
||||
Commands.AutoFetch.RemoveRepository(repo.FullPath);
|
||||
repo.Close();
|
||||
}
|
||||
|
@ -187,4 +232,4 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
private LauncherPage _activePage = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,33 @@
|
|||
using Avalonia.Collections;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class LauncherPage : PopupHost {
|
||||
public RepositoryNode Node {
|
||||
using Avalonia.Collections;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class LauncherPage : PopupHost
|
||||
{
|
||||
public RepositoryNode Node
|
||||
{
|
||||
get => _node;
|
||||
set => SetProperty(ref _node, value);
|
||||
}
|
||||
|
||||
public object Data {
|
||||
public object Data
|
||||
{
|
||||
get => _data;
|
||||
set => SetProperty(ref _data, value);
|
||||
}
|
||||
|
||||
public AvaloniaList<Models.Notification> Notifications {
|
||||
public AvaloniaList<Models.Notification> Notifications
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new AvaloniaList<Models.Notification>();
|
||||
|
||||
public LauncherPage() {
|
||||
_node = new RepositoryNode() {
|
||||
public LauncherPage()
|
||||
{
|
||||
_node = new RepositoryNode()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "WelcomePage",
|
||||
Bookmark = 0,
|
||||
|
@ -28,21 +36,26 @@ namespace SourceGit.ViewModels {
|
|||
_data = new Welcome();
|
||||
}
|
||||
|
||||
public LauncherPage(RepositoryNode node, Repository repo) {
|
||||
public LauncherPage(RepositoryNode node, Repository repo)
|
||||
{
|
||||
_node = node;
|
||||
_data = repo;
|
||||
}
|
||||
|
||||
public override string GetId() {
|
||||
public override string GetId()
|
||||
{
|
||||
return _node.Id;
|
||||
}
|
||||
|
||||
public void CopyPath() {
|
||||
public void CopyPath()
|
||||
{
|
||||
if (_node.IsRepository) App.CopyText(_node.Id);
|
||||
}
|
||||
|
||||
public void DismissNotification(object param) {
|
||||
if (param is Models.Notification notice) {
|
||||
public void DismissNotification(object param)
|
||||
{
|
||||
if (param is Models.Notification notice)
|
||||
{
|
||||
Notifications.Remove(notice);
|
||||
}
|
||||
}
|
||||
|
@ -50,4 +63,4 @@ namespace SourceGit.ViewModels {
|
|||
private RepositoryNode _node = null;
|
||||
private object _data = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class MergeMode {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class MergeMode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public string Arg { get; set; }
|
||||
|
||||
public MergeMode(string n, string d, string a) {
|
||||
public MergeMode(string n, string d, string a)
|
||||
{
|
||||
Name = n;
|
||||
Desc = d;
|
||||
Arg = a;
|
||||
}
|
||||
}
|
||||
|
||||
public class Merge : Popup {
|
||||
public string Source {
|
||||
public class Merge : Popup
|
||||
{
|
||||
public string Source
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string Into {
|
||||
public string Into
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<MergeMode> Modes {
|
||||
public List<MergeMode> Modes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public MergeMode SelectedMode {
|
||||
public MergeMode SelectedMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Merge(Repository repo, string source, string into) {
|
||||
public Merge(Repository repo, string source, string into)
|
||||
{
|
||||
_repo = repo;
|
||||
Source = source;
|
||||
Into = into;
|
||||
|
@ -49,17 +58,19 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Merge() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Merging '{Source}' into '{Into}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Merge(_repo.FullPath, Source, SelectedMode.Arg, SetProgressDescription).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +1,63 @@
|
|||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Popup : ObservableValidator {
|
||||
public string HostPageId {
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Popup : ObservableValidator
|
||||
{
|
||||
public string HostPageId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public object View {
|
||||
public object View
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool InProgress {
|
||||
public bool InProgress
|
||||
{
|
||||
get => _inProgress;
|
||||
set => SetProperty(ref _inProgress, value);
|
||||
}
|
||||
|
||||
public string ProgressDescription {
|
||||
public string ProgressDescription
|
||||
{
|
||||
get => _progressDescription;
|
||||
set => SetProperty(ref _progressDescription, value);
|
||||
}
|
||||
|
||||
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode")]
|
||||
public bool Check() {
|
||||
public bool Check()
|
||||
{
|
||||
if (HasErrors) return false;
|
||||
ValidateAllProperties();
|
||||
return !HasErrors;
|
||||
}
|
||||
|
||||
public virtual Task<bool> Sure() {
|
||||
public virtual Task<bool> Sure()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void CallUIThread(Action action) {
|
||||
protected void CallUIThread(Action action)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(action);
|
||||
}
|
||||
|
||||
protected void SetProgressDescription(string description) {
|
||||
protected void SetProgressDescription(string description)
|
||||
{
|
||||
CallUIThread(() => ProgressDescription = description);
|
||||
}
|
||||
|
||||
private bool _inProgress = false;
|
||||
private string _progressDescription = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +1,74 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class PopupHost : ObservableObject {
|
||||
public static PopupHost Active {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class PopupHost : ObservableObject
|
||||
{
|
||||
public static PopupHost Active
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = null;
|
||||
|
||||
public Popup Popup {
|
||||
public Popup Popup
|
||||
{
|
||||
get => _popup;
|
||||
set => SetProperty(ref _popup, value);
|
||||
}
|
||||
|
||||
public static bool CanCreatePopup() {
|
||||
public static bool CanCreatePopup()
|
||||
{
|
||||
return Active != null && (Active._popup == null || !Active._popup.InProgress);
|
||||
}
|
||||
|
||||
public static void ShowPopup(Popup popup) {
|
||||
public static void ShowPopup(Popup popup)
|
||||
{
|
||||
popup.HostPageId = Active.GetId();
|
||||
Active.Popup = popup;
|
||||
}
|
||||
|
||||
public static void ShowAndStartPopup(Popup popup) {
|
||||
public static void ShowAndStartPopup(Popup popup)
|
||||
{
|
||||
var dumpPage = Active;
|
||||
popup.HostPageId = dumpPage.GetId();
|
||||
dumpPage.Popup = popup;
|
||||
dumpPage.ProcessPopup();
|
||||
}
|
||||
|
||||
public virtual string GetId() {
|
||||
public virtual string GetId()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async void ProcessPopup() {
|
||||
if (_popup != null) {
|
||||
public async void ProcessPopup()
|
||||
{
|
||||
if (_popup != null)
|
||||
{
|
||||
if (!_popup.Check()) return;
|
||||
|
||||
_popup.InProgress = true;
|
||||
var task = _popup.Sure();
|
||||
if (task != null) {
|
||||
if (task != null)
|
||||
{
|
||||
var finished = await task;
|
||||
if (finished) {
|
||||
if (finished)
|
||||
{
|
||||
Popup = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_popup.InProgress = false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Popup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelPopup() {
|
||||
public void CancelPopup()
|
||||
{
|
||||
if (_popup == null) return;
|
||||
if (_popup.InProgress) return;
|
||||
Popup = null;
|
||||
|
@ -59,4 +76,4 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
private Popup _popup = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,36 @@
|
|||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Preference : ObservableObject {
|
||||
using Avalonia.Collections;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Preference : ObservableObject
|
||||
{
|
||||
[JsonIgnore]
|
||||
public static Preference Instance {
|
||||
get {
|
||||
if (_instance == null) {
|
||||
if (!File.Exists(_savePath)) {
|
||||
public static Preference Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
if (!File.Exists(_savePath))
|
||||
{
|
||||
_instance = new Preference();
|
||||
} else {
|
||||
try {
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_instance = JsonSerializer.Deserialize(File.ReadAllText(_savePath), JsonSerializationCodeGen.Default.Preference);
|
||||
} catch {
|
||||
}
|
||||
catch
|
||||
{
|
||||
_instance = new Preference();
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +38,8 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
_instance.Repositories.RemoveAll(x => !Directory.Exists(x.FullPath));
|
||||
|
||||
if (!_instance.IsGitConfigured) {
|
||||
if (!_instance.IsGitConfigured)
|
||||
{
|
||||
_instance.GitInstallPath = Native.OS.FindGitExecutable();
|
||||
}
|
||||
|
||||
|
@ -33,109 +47,137 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public string Locale {
|
||||
public string Locale
|
||||
{
|
||||
get => _locale;
|
||||
set {
|
||||
if (SetProperty(ref _locale, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _locale, value))
|
||||
{
|
||||
App.SetLocale(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Theme {
|
||||
public string Theme
|
||||
{
|
||||
get => _theme;
|
||||
set {
|
||||
if (SetProperty(ref _theme, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _theme, value))
|
||||
{
|
||||
App.SetTheme(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string AvatarServer {
|
||||
public string AvatarServer
|
||||
{
|
||||
get => Models.AvatarManager.SelectedServer;
|
||||
set {
|
||||
if (Models.AvatarManager.SelectedServer != value) {
|
||||
set
|
||||
{
|
||||
if (Models.AvatarManager.SelectedServer != value)
|
||||
{
|
||||
Models.AvatarManager.SelectedServer = value;
|
||||
OnPropertyChanged(nameof(AvatarServer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxHistoryCommits {
|
||||
public int MaxHistoryCommits
|
||||
{
|
||||
get => _maxHistoryCommits;
|
||||
set => SetProperty(ref _maxHistoryCommits, value);
|
||||
}
|
||||
|
||||
public bool RestoreTabs {
|
||||
public bool RestoreTabs
|
||||
{
|
||||
get => _restoreTabs;
|
||||
set => SetProperty(ref _restoreTabs, value);
|
||||
}
|
||||
|
||||
public bool UseFixedTabWidth {
|
||||
public bool UseFixedTabWidth
|
||||
{
|
||||
get => _useFixedTabWidth;
|
||||
set => SetProperty(ref _useFixedTabWidth, value);
|
||||
}
|
||||
|
||||
public bool UseTwoColumnsLayoutInHistories {
|
||||
public bool UseTwoColumnsLayoutInHistories
|
||||
{
|
||||
get => _useTwoColumnsLayoutInHistories;
|
||||
set => SetProperty(ref _useTwoColumnsLayoutInHistories, value);
|
||||
}
|
||||
|
||||
public bool UseCombinedTextDiff {
|
||||
public bool UseCombinedTextDiff
|
||||
{
|
||||
get => _useCombinedTextDiff;
|
||||
set => SetProperty(ref _useCombinedTextDiff, value);
|
||||
}
|
||||
|
||||
public Models.ChangeViewMode UnstagedChangeViewMode {
|
||||
public Models.ChangeViewMode UnstagedChangeViewMode
|
||||
{
|
||||
get => _unstagedChangeViewMode;
|
||||
set => SetProperty(ref _unstagedChangeViewMode, value);
|
||||
}
|
||||
|
||||
public Models.ChangeViewMode StagedChangeViewMode {
|
||||
public Models.ChangeViewMode StagedChangeViewMode
|
||||
{
|
||||
get => _stagedChangeViewMode;
|
||||
set => SetProperty(ref _stagedChangeViewMode, value);
|
||||
}
|
||||
|
||||
public Models.ChangeViewMode CommitChangeViewMode {
|
||||
public Models.ChangeViewMode CommitChangeViewMode
|
||||
{
|
||||
get => _commitChangeViewMode;
|
||||
set => SetProperty(ref _commitChangeViewMode, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsGitConfigured {
|
||||
public bool IsGitConfigured
|
||||
{
|
||||
get => !string.IsNullOrEmpty(GitInstallPath) && File.Exists(GitInstallPath);
|
||||
}
|
||||
|
||||
public string GitInstallPath {
|
||||
public string GitInstallPath
|
||||
{
|
||||
get => Native.OS.GitInstallPath;
|
||||
set {
|
||||
if (Native.OS.GitInstallPath != value) {
|
||||
set
|
||||
{
|
||||
if (Native.OS.GitInstallPath != value)
|
||||
{
|
||||
Native.OS.GitInstallPath = value;
|
||||
OnPropertyChanged(nameof(GitInstallPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GitDefaultCloneDir {
|
||||
public string GitDefaultCloneDir
|
||||
{
|
||||
get => _gitDefaultCloneDir;
|
||||
set => SetProperty(ref _gitDefaultCloneDir, value);
|
||||
}
|
||||
|
||||
public bool GitAutoFetch {
|
||||
public bool GitAutoFetch
|
||||
{
|
||||
get => Commands.AutoFetch.IsEnabled;
|
||||
set {
|
||||
if (Commands.AutoFetch.IsEnabled != value) {
|
||||
set
|
||||
{
|
||||
if (Commands.AutoFetch.IsEnabled != value)
|
||||
{
|
||||
Commands.AutoFetch.IsEnabled = value;
|
||||
OnPropertyChanged(nameof(GitAutoFetch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ExternalMergeToolType {
|
||||
public int ExternalMergeToolType
|
||||
{
|
||||
get => _externalMergeToolType;
|
||||
set {
|
||||
set
|
||||
{
|
||||
var changed = SetProperty(ref _externalMergeToolType, value);
|
||||
if (changed && !OperatingSystem.IsWindows() && value > 0 && value < Models.ExternalMergeTools.Supported.Count) {
|
||||
if (changed && !OperatingSystem.IsWindows() && value > 0 && value < Models.ExternalMergeTools.Supported.Count)
|
||||
{
|
||||
var tool = Models.ExternalMergeTools.Supported[value];
|
||||
if (File.Exists(tool.Exec)) ExternalMergeToolPath = tool.Exec;
|
||||
else ExternalMergeToolPath = string.Empty;
|
||||
|
@ -143,65 +185,80 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public string ExternalMergeToolPath {
|
||||
public string ExternalMergeToolPath
|
||||
{
|
||||
get => _externalMergeToolPath;
|
||||
set => SetProperty(ref _externalMergeToolPath, value);
|
||||
}
|
||||
|
||||
public string ExternalMergeToolCmd {
|
||||
public string ExternalMergeToolCmd
|
||||
{
|
||||
get => _externalMergeToolCmd;
|
||||
set => SetProperty(ref _externalMergeToolCmd, value);
|
||||
}
|
||||
|
||||
public string ExternalMergeToolDiffCmd {
|
||||
public string ExternalMergeToolDiffCmd
|
||||
{
|
||||
get => _externalMergeToolDiffCmd;
|
||||
set => SetProperty(ref _externalMergeToolDiffCmd, value);
|
||||
}
|
||||
|
||||
public List<Repository> Repositories {
|
||||
public List<Repository> Repositories
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new List<Repository>();
|
||||
|
||||
public AvaloniaList<RepositoryNode> RepositoryNodes {
|
||||
public AvaloniaList<RepositoryNode> RepositoryNodes
|
||||
{
|
||||
get => _repositoryNodes;
|
||||
set => SetProperty(ref _repositoryNodes, value);
|
||||
}
|
||||
|
||||
public List<string> OpenedTabs {
|
||||
public List<string> OpenedTabs
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new List<string>();
|
||||
|
||||
public int LastActiveTabIdx {
|
||||
public int LastActiveTabIdx
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = 0;
|
||||
|
||||
public static void AddNode(RepositoryNode node, RepositoryNode to = null) {
|
||||
public static void AddNode(RepositoryNode node, RepositoryNode to = null)
|
||||
{
|
||||
var collection = to == null ? _instance._repositoryNodes : to.SubNodes;
|
||||
var list = new List<RepositoryNode>();
|
||||
list.AddRange(collection);
|
||||
list.Add(node);
|
||||
list.Sort((l, r) => {
|
||||
if (l.IsRepository != r.IsRepository) {
|
||||
list.Sort((l, r) =>
|
||||
{
|
||||
if (l.IsRepository != r.IsRepository)
|
||||
{
|
||||
return l.IsRepository ? 1 : -1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return l.Name.CompareTo(r.Name);
|
||||
}
|
||||
});
|
||||
|
||||
collection.Clear();
|
||||
foreach (var one in list) {
|
||||
foreach (var one in list)
|
||||
{
|
||||
collection.Add(one);
|
||||
}
|
||||
}
|
||||
|
||||
public static RepositoryNode FindNode(string id) {
|
||||
public static RepositoryNode FindNode(string id)
|
||||
{
|
||||
return FindNodeRecursive(id, _instance.RepositoryNodes);
|
||||
}
|
||||
|
||||
public static void MoveNode(RepositoryNode node, RepositoryNode to = null) {
|
||||
public static void MoveNode(RepositoryNode node, RepositoryNode to = null)
|
||||
{
|
||||
if (to == null && _instance._repositoryNodes.Contains(node)) return;
|
||||
if (to != null && to.SubNodes.Contains(node)) return;
|
||||
|
||||
|
@ -209,26 +266,32 @@ namespace SourceGit.ViewModels {
|
|||
AddNode(node, to);
|
||||
}
|
||||
|
||||
public static void RemoveNode(RepositoryNode node) {
|
||||
public static void RemoveNode(RepositoryNode node)
|
||||
{
|
||||
RemoveNodeRecursive(node, _instance._repositoryNodes);
|
||||
}
|
||||
|
||||
public static Repository FindRepository(string path) {
|
||||
foreach (var repo in _instance.Repositories) {
|
||||
public static Repository FindRepository(string path)
|
||||
{
|
||||
foreach (var repo in _instance.Repositories)
|
||||
{
|
||||
if (repo.FullPath == path) return repo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Repository AddRepository(string rootDir, string gitDir) {
|
||||
public static Repository AddRepository(string rootDir, string gitDir)
|
||||
{
|
||||
var normalized = rootDir.Replace('\\', '/');
|
||||
var repo = FindRepository(normalized);
|
||||
if (repo != null) {
|
||||
if (repo != null)
|
||||
{
|
||||
repo.GitDir = gitDir;
|
||||
return repo;
|
||||
}
|
||||
|
||||
repo = new Repository() {
|
||||
repo = new Repository()
|
||||
{
|
||||
FullPath = normalized,
|
||||
GitDir = gitDir
|
||||
};
|
||||
|
@ -237,7 +300,8 @@ namespace SourceGit.ViewModels {
|
|||
return repo;
|
||||
}
|
||||
|
||||
public static void Save() {
|
||||
public static void Save()
|
||||
{
|
||||
var dir = Path.GetDirectoryName(_savePath);
|
||||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||
|
||||
|
@ -245,8 +309,10 @@ namespace SourceGit.ViewModels {
|
|||
File.WriteAllText(_savePath, data);
|
||||
}
|
||||
|
||||
private static RepositoryNode FindNodeRecursive(string id, AvaloniaList<RepositoryNode> collection) {
|
||||
foreach (var node in collection) {
|
||||
private static RepositoryNode FindNodeRecursive(string id, AvaloniaList<RepositoryNode> collection)
|
||||
{
|
||||
foreach (var node in collection)
|
||||
{
|
||||
if (node.Id == id) return node;
|
||||
|
||||
var sub = FindNodeRecursive(id, node.SubNodes);
|
||||
|
@ -256,13 +322,16 @@ namespace SourceGit.ViewModels {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static bool RemoveNodeRecursive(RepositoryNode node, AvaloniaList<RepositoryNode> collection) {
|
||||
if (collection.Contains(node)) {
|
||||
private static bool RemoveNodeRecursive(RepositoryNode node, AvaloniaList<RepositoryNode> collection)
|
||||
{
|
||||
if (collection.Contains(node))
|
||||
{
|
||||
collection.Remove(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (RepositoryNode one in collection) {
|
||||
foreach (RepositoryNode one in collection)
|
||||
{
|
||||
if (RemoveNodeRecursive(node, one.SubNodes)) return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +1,35 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class PruneRemote : Popup {
|
||||
public Models.Remote Remote {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class PruneRemote : Popup
|
||||
{
|
||||
public Models.Remote Remote
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public PruneRemote(Repository repo, Models.Remote remote) {
|
||||
public PruneRemote(Repository repo, Models.Remote remote)
|
||||
{
|
||||
_repo = repo;
|
||||
Remote = remote;
|
||||
View = new Views.PruneRemote() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Run `prune` on remote ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Remote(_repo.FullPath).Prune(Remote.Name);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,22 +2,29 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Pull : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Pull : Popup
|
||||
{
|
||||
public List<Models.Remote> Remotes => _repo.Remotes;
|
||||
public Models.Branch Current => _current;
|
||||
|
||||
public bool HasSpecifiedRemoteBranch {
|
||||
public bool HasSpecifiedRemoteBranch
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Remote SelectedRemote {
|
||||
public Models.Remote SelectedRemote
|
||||
{
|
||||
get => _selectedRemote;
|
||||
set {
|
||||
if (SetProperty(ref _selectedRemote, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedRemote, value))
|
||||
{
|
||||
var branches = new List<Models.Branch>();
|
||||
foreach (var branch in _repo.Branches) {
|
||||
foreach (var branch in _repo.Branches)
|
||||
{
|
||||
if (branch.Remote == value.Name) branches.Add(branch);
|
||||
}
|
||||
RemoteBranches = branches;
|
||||
|
@ -26,81 +33,100 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Models.Branch> RemoteBranches {
|
||||
public List<Models.Branch> RemoteBranches
|
||||
{
|
||||
get => _remoteBranches;
|
||||
private set => SetProperty(ref _remoteBranches, value);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Remote branch to pull is required!!!")]
|
||||
public Models.Branch SelectedBranch {
|
||||
public Models.Branch SelectedBranch
|
||||
{
|
||||
get => _selectedBranch;
|
||||
set => SetProperty(ref _selectedBranch, value);
|
||||
}
|
||||
|
||||
public bool UseRebase {
|
||||
public bool UseRebase
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = true;
|
||||
|
||||
public bool AutoStash {
|
||||
public bool AutoStash
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = true;
|
||||
|
||||
public Pull(Repository repo, Models.Branch specifiedRemoteBranch) {
|
||||
public Pull(Repository repo, Models.Branch specifiedRemoteBranch)
|
||||
{
|
||||
_repo = repo;
|
||||
_current = repo.Branches.Find(x => x.IsCurrent);
|
||||
|
||||
if (specifiedRemoteBranch != null) {
|
||||
|
||||
if (specifiedRemoteBranch != null)
|
||||
{
|
||||
_selectedRemote = repo.Remotes.Find(x => x.Name == specifiedRemoteBranch.Remote);
|
||||
_selectedBranch = specifiedRemoteBranch;
|
||||
HasSpecifiedRemoteBranch = true;
|
||||
} else {
|
||||
if (!string.IsNullOrEmpty(_current.Upstream)) {
|
||||
foreach (var branch in repo.Branches) {
|
||||
if (!branch.IsLocal && _current.Upstream == branch.FullName) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_current.Upstream))
|
||||
{
|
||||
foreach (var branch in repo.Branches)
|
||||
{
|
||||
if (!branch.IsLocal && _current.Upstream == branch.FullName)
|
||||
{
|
||||
_selectedRemote = repo.Remotes.Find(x => x.Name == branch.Remote);
|
||||
_selectedBranch = branch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HasSpecifiedRemoteBranch = false;
|
||||
}
|
||||
|
||||
// Make sure remote is exists.
|
||||
if (_selectedRemote == null) {
|
||||
if (_selectedRemote == null)
|
||||
{
|
||||
_selectedRemote = repo.Remotes[0];
|
||||
_selectedBranch = null;
|
||||
HasSpecifiedRemoteBranch = false;
|
||||
}
|
||||
|
||||
_remoteBranches = new List<Models.Branch>();
|
||||
foreach (var branch in _repo.Branches) {
|
||||
foreach (var branch in _repo.Branches)
|
||||
{
|
||||
if (branch.Remote == _selectedRemote.Name) _remoteBranches.Add(branch);
|
||||
}
|
||||
|
||||
if (_selectedBranch == null && _remoteBranches.Count > 0) {
|
||||
if (_selectedBranch == null && _remoteBranches.Count > 0)
|
||||
{
|
||||
_selectedBranch = _remoteBranches[0];
|
||||
}
|
||||
|
||||
View = new Views.Pull() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var needPopStash = false;
|
||||
if (AutoStash && _repo.WorkingCopyChangesCount > 0) {
|
||||
if (AutoStash && _repo.WorkingCopyChangesCount > 0)
|
||||
{
|
||||
SetProgressDescription("Adding untracked changes...");
|
||||
var succ = new Commands.Add(_repo.FullPath).Exec();
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
SetProgressDescription("Stash local changes...");
|
||||
succ = new Commands.Stash(_repo.FullPath).Push("PULL_AUTO_STASH");
|
||||
}
|
||||
|
||||
if (!succ) {
|
||||
|
||||
if (!succ)
|
||||
{
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return false;
|
||||
}
|
||||
|
@ -110,7 +136,8 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
|
||||
var rs = new Commands.Pull(_repo.FullPath, _selectedRemote.Name, _selectedBranch.Name, UseRebase, SetProgressDescription).Exec();
|
||||
if (rs && needPopStash) {
|
||||
if (rs && needPopStash)
|
||||
{
|
||||
SetProgressDescription("Re-apply local changes...");
|
||||
rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
|
||||
}
|
||||
|
@ -120,10 +147,10 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private Models.Branch _current = null;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.Branch _current = null;
|
||||
private Models.Remote _selectedRemote = null;
|
||||
private List<Models.Branch> _remoteBranches = null;
|
||||
private Models.Branch _selectedBranch = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,24 +2,33 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Push : Popup {
|
||||
public bool HasSpecifiedLocalBranch {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Push : Popup
|
||||
{
|
||||
public bool HasSpecifiedLocalBranch
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Local branch is required!!!")]
|
||||
public Models.Branch SelectedLocalBranch {
|
||||
public Models.Branch SelectedLocalBranch
|
||||
{
|
||||
get => _selectedLocalBranch;
|
||||
set {
|
||||
if (SetProperty(ref _selectedLocalBranch, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedLocalBranch, value))
|
||||
{
|
||||
// If selected local branch has upstream branch. Try to find it's remote.
|
||||
if (!string.IsNullOrEmpty(value.Upstream)) {
|
||||
if (!string.IsNullOrEmpty(value.Upstream))
|
||||
{
|
||||
var branch = _repo.Branches.Find(x => x.FullName == value.Upstream);
|
||||
if (branch != null) {
|
||||
if (branch != null)
|
||||
{
|
||||
var remote = _repo.Remotes.Find(x => x.Name == branch.Remote);
|
||||
if (remote != null && remote != _selectedRemote) {
|
||||
if (remote != null && remote != _selectedRemote)
|
||||
{
|
||||
SelectedRemote = remote;
|
||||
return;
|
||||
}
|
||||
|
@ -32,68 +41,84 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Models.Branch> LocalBranches {
|
||||
public List<Models.Branch> LocalBranches
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Models.Remote> Remotes {
|
||||
public List<Models.Remote> Remotes
|
||||
{
|
||||
get => _repo.Remotes;
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Remote is required!!!")]
|
||||
public Models.Remote SelectedRemote {
|
||||
public Models.Remote SelectedRemote
|
||||
{
|
||||
get => _selectedRemote;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedRemote, value)) AutoSelectBranchByRemote();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Models.Branch> RemoteBranches {
|
||||
public List<Models.Branch> RemoteBranches
|
||||
{
|
||||
get => _remoteBranches;
|
||||
private set => SetProperty(ref _remoteBranches, value);
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Remote branch is required!!!")]
|
||||
public Models.Branch SelectedRemoteBranch {
|
||||
public Models.Branch SelectedRemoteBranch
|
||||
{
|
||||
get => _selectedRemoteBranch;
|
||||
set => SetProperty(ref _selectedRemoteBranch, value);
|
||||
}
|
||||
|
||||
public bool PushAllTags {
|
||||
public bool PushAllTags
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ForcePush {
|
||||
public bool ForcePush
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Push(Repository repo, Models.Branch localBranch) {
|
||||
public Push(Repository repo, Models.Branch localBranch)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
// Gather all local branches and find current branch.
|
||||
LocalBranches = new List<Models.Branch>();
|
||||
var current = null as Models.Branch;
|
||||
foreach (var branch in _repo.Branches) {
|
||||
foreach (var branch in _repo.Branches)
|
||||
{
|
||||
if (branch.IsLocal) LocalBranches.Add(branch);
|
||||
if (branch.IsCurrent) current = branch;
|
||||
}
|
||||
|
||||
// Set default selected local branch.
|
||||
if (localBranch != null) {
|
||||
if (localBranch != null)
|
||||
{
|
||||
_selectedLocalBranch = localBranch;
|
||||
HasSpecifiedLocalBranch = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedLocalBranch = current;
|
||||
HasSpecifiedLocalBranch = false;
|
||||
}
|
||||
|
||||
// Find preferred remote if selected local branch has upstream.
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream)) {
|
||||
foreach (var branch in repo.Branches) {
|
||||
if (!branch.IsLocal && _selectedLocalBranch.Upstream == branch.FullName) {
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream))
|
||||
{
|
||||
foreach (var branch in repo.Branches)
|
||||
{
|
||||
if (!branch.IsLocal && _selectedLocalBranch.Upstream == branch.FullName)
|
||||
{
|
||||
_selectedRemote = repo.Remotes.Find(x => x.Name == branch.Remote);
|
||||
break;
|
||||
}
|
||||
|
@ -109,13 +134,15 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Push() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
|
||||
var remoteBranchName = _selectedRemoteBranch.Name.Replace(" (new)", "");
|
||||
ProgressDescription = $"Push {_selectedLocalBranch.Name} -> {_selectedRemote.Name}/{remoteBranchName} ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Push(
|
||||
_repo.FullPath,
|
||||
_selectedLocalBranch.Name,
|
||||
|
@ -130,17 +157,22 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private void AutoSelectBranchByRemote() {
|
||||
private void AutoSelectBranchByRemote()
|
||||
{
|
||||
// Gather branches.
|
||||
var branches = new List<Models.Branch>();
|
||||
foreach (var branch in _repo.Branches) {
|
||||
foreach (var branch in _repo.Branches)
|
||||
{
|
||||
if (branch.Remote == _selectedRemote.Name) branches.Add(branch);
|
||||
}
|
||||
|
||||
// If selected local branch has upstream branch. Try to find it in current remote branches.
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream)) {
|
||||
foreach (var branch in branches) {
|
||||
if (_selectedLocalBranch.Upstream == branch.FullName) {
|
||||
if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream))
|
||||
{
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (_selectedLocalBranch.Upstream == branch.FullName)
|
||||
{
|
||||
RemoteBranches = branches;
|
||||
SelectedRemoteBranch = branch;
|
||||
return;
|
||||
|
@ -149,8 +181,10 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
|
||||
// Find best remote branch by name.
|
||||
foreach (var branch in branches) {
|
||||
if (_selectedLocalBranch.Name == branch.Name) {
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (_selectedLocalBranch.Name == branch.Name)
|
||||
{
|
||||
RemoteBranches = branches;
|
||||
SelectedRemoteBranch = branch;
|
||||
return;
|
||||
|
@ -158,7 +192,8 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
|
||||
// Add a fake new branch.
|
||||
var fake = new Models.Branch() {
|
||||
var fake = new Models.Branch()
|
||||
{
|
||||
Name = $"{_selectedLocalBranch.Name} (new)",
|
||||
Remote = _selectedRemote.Name,
|
||||
};
|
||||
|
@ -167,10 +202,10 @@ namespace SourceGit.ViewModels {
|
|||
SelectedRemoteBranch = fake;
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private Models.Branch _selectedLocalBranch = null;
|
||||
private Models.Remote _selectedRemote = null;
|
||||
private List<Models.Branch> _remoteBranches = new List<Models.Branch>();
|
||||
private Models.Branch _selectedRemoteBranch = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class PushTag : Popup {
|
||||
public Models.Tag Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class PushTag : Popup
|
||||
{
|
||||
public Models.Tag Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Models.Remote> Remotes {
|
||||
public List<Models.Remote> Remotes
|
||||
{
|
||||
get => _repo.Remotes;
|
||||
}
|
||||
|
||||
public Models.Remote SelectedRemote {
|
||||
public Models.Remote SelectedRemote
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public PushTag(Repository repo, Models.Tag target) {
|
||||
public PushTag(Repository repo, Models.Tag target)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = target;
|
||||
SelectedRemote = _repo.Remotes[0];
|
||||
View = new Views.PushTag() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Pushing tag '{Target.Name}' to remote '{SelectedRemote.Name}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Push(_repo.FullPath, SelectedRemote.Name, Target.Name, false).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,29 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Rebase : Popup {
|
||||
public Models.Branch Current {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Rebase : Popup
|
||||
{
|
||||
public Models.Branch Current
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public object On {
|
||||
public object On
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool AutoStash {
|
||||
public bool AutoStash
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Rebase(Repository repo, Models.Branch current, Models.Branch on) {
|
||||
public Rebase(Repository repo, Models.Branch current, Models.Branch on)
|
||||
{
|
||||
_repo = repo;
|
||||
_revision = on.Head;
|
||||
Current = current;
|
||||
|
@ -26,7 +32,8 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Rebase() { DataContext = this };
|
||||
}
|
||||
|
||||
public Rebase(Repository repo, Models.Branch current, Models.Commit on) {
|
||||
public Rebase(Repository repo, Models.Branch current, Models.Commit on)
|
||||
{
|
||||
_repo = repo;
|
||||
_revision = on.SHA;
|
||||
Current = current;
|
||||
|
@ -35,18 +42,20 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Rebase() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Rebasing ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Rebase(_repo.FullPath, _revision, AutoStash).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private string _revision = string.Empty;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly string _revision = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class RenameBranch : Popup {
|
||||
public Models.Branch Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class RenameBranch : Popup
|
||||
{
|
||||
public Models.Branch Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
@ -11,22 +14,28 @@ namespace SourceGit.ViewModels {
|
|||
[Required(ErrorMessage = "Branch name is required!!!")]
|
||||
[RegularExpression(@"^[\w\-/\.]+$", ErrorMessage = "Bad branch name format!")]
|
||||
[CustomValidation(typeof(RenameBranch), nameof(ValidateBranchName))]
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value, true);
|
||||
}
|
||||
|
||||
public RenameBranch(Repository repo, Models.Branch target) {
|
||||
public RenameBranch(Repository repo, Models.Branch target)
|
||||
{
|
||||
_repo = repo;
|
||||
_name = target.Name;
|
||||
Target = target;
|
||||
View = new Views.RenameBranch() { DataContext = this };
|
||||
}
|
||||
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx) {
|
||||
if (ctx.ObjectInstance is RenameBranch rename) {
|
||||
foreach (var b in rename._repo.Branches) {
|
||||
if (b != rename.Target && b.Name == name) {
|
||||
public static ValidationResult ValidateBranchName(string name, ValidationContext ctx)
|
||||
{
|
||||
if (ctx.ObjectInstance is RenameBranch rename)
|
||||
{
|
||||
foreach (var b in rename._repo.Branches)
|
||||
{
|
||||
if (b != rename.Target && b.Name == name)
|
||||
{
|
||||
return new ValidationResult("A branch with same name already exists!!!");
|
||||
}
|
||||
}
|
||||
|
@ -35,20 +44,22 @@ namespace SourceGit.ViewModels {
|
|||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
if (_name == Target.Name) return null;
|
||||
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Rename '{Target.Name}'";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, _name);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _name = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,34 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class RepositoryConfigure : Popup {
|
||||
public string UserName {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class RepositoryConfigure : Popup
|
||||
{
|
||||
public string UserName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string UserEmail {
|
||||
public string UserEmail
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool GPGSigningEnabled {
|
||||
public bool GPGSigningEnabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string GPGUserSigningKey {
|
||||
public string GPGUserSigningKey
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string HttpProxy {
|
||||
public string HttpProxy
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RepositoryConfigure(Repository repo) {
|
||||
public RepositoryConfigure(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
_cached = new Commands.Config(repo.FullPath).ListAll();
|
||||
|
@ -41,7 +49,8 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.RepositoryConfigure() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
SetIfChanged("user.name", UserName);
|
||||
SetIfChanged("user.email", UserEmail);
|
||||
SetIfChanged("commit.gpgsign", GPGSigningEnabled ? "true" : "false");
|
||||
|
@ -50,20 +59,25 @@ namespace SourceGit.ViewModels {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void SetIfChanged(string key, string value) {
|
||||
private void SetIfChanged(string key, string value)
|
||||
{
|
||||
bool changed = false;
|
||||
if (_cached.ContainsKey(key)) {
|
||||
if (_cached.ContainsKey(key))
|
||||
{
|
||||
changed = value != _cached[key];
|
||||
} else if (!string.IsNullOrEmpty(value)) {
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (changed)
|
||||
{
|
||||
new Commands.Config(_repo.FullPath).Set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private Dictionary<string, string> _cached = null;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Dictionary<string, string> _cached = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,67 +1,84 @@
|
|||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class RepositoryNode : ObservableObject {
|
||||
public string Id {
|
||||
using Avalonia.Collections;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class RepositoryNode : ObservableObject
|
||||
{
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
set {
|
||||
set
|
||||
{
|
||||
var normalized = value.Replace('\\', '/');
|
||||
SetProperty(ref _id, normalized);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name {
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, value);
|
||||
}
|
||||
|
||||
public int Bookmark {
|
||||
public int Bookmark
|
||||
{
|
||||
get => _bookmark;
|
||||
set => SetProperty(ref _bookmark, value);
|
||||
}
|
||||
|
||||
public bool IsRepository {
|
||||
public bool IsRepository
|
||||
{
|
||||
get => _isRepository;
|
||||
set => SetProperty(ref _isRepository, value);
|
||||
}
|
||||
|
||||
public bool IsExpanded {
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetProperty(ref _isExpanded, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsVisible {
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
set => SetProperty(ref _isVisible, value);
|
||||
}
|
||||
|
||||
public AvaloniaList<RepositoryNode> SubNodes {
|
||||
|
||||
public AvaloniaList<RepositoryNode> SubNodes
|
||||
{
|
||||
get => _subNodes;
|
||||
set => SetProperty(ref _subNodes, value);
|
||||
}
|
||||
|
||||
public void Edit() {
|
||||
public void Edit()
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new EditRepositoryNode(this));
|
||||
}
|
||||
|
||||
public void AddSubFolder() {
|
||||
public void AddSubFolder()
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateGroup(this));
|
||||
}
|
||||
|
||||
public void OpenInFileManager() {
|
||||
public void OpenInFileManager()
|
||||
{
|
||||
if (!IsRepository) return;
|
||||
Native.OS.OpenInFileManager(_id);
|
||||
}
|
||||
|
||||
public void OpenTerminal() {
|
||||
public void OpenTerminal()
|
||||
{
|
||||
if (!IsRepository) return;
|
||||
Native.OS.OpenTerminal(_id);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
public void Delete()
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteRepositoryNode(this));
|
||||
}
|
||||
|
||||
|
@ -73,4 +90,4 @@ namespace SourceGit.ViewModels {
|
|||
private bool _isVisible = true;
|
||||
private AvaloniaList<RepositoryNode> _subNodes = new AvaloniaList<RepositoryNode>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,19 @@
|
|||
using Avalonia.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class ResetMode {
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class ResetMode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Desc { get; set; }
|
||||
public string Arg { get; set; }
|
||||
public IBrush Color { get; set; }
|
||||
|
||||
public ResetMode(string n, string d, string a, IBrush b) {
|
||||
public ResetMode(string n, string d, string a, IBrush b)
|
||||
{
|
||||
Name = n;
|
||||
Desc = d;
|
||||
Arg = a;
|
||||
|
@ -17,28 +21,34 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public class Reset : Popup {
|
||||
public Models.Branch Current {
|
||||
public class Reset : Popup
|
||||
{
|
||||
public Models.Branch Current
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Commit To {
|
||||
public Models.Commit To
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<ResetMode> Modes {
|
||||
public List<ResetMode> Modes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ResetMode SelectedMode {
|
||||
public ResetMode SelectedMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Reset(Repository repo, Models.Branch current, Models.Commit to) {
|
||||
public Reset(Repository repo, Models.Branch current, Models.Commit to)
|
||||
{
|
||||
_repo = repo;
|
||||
Current = current;
|
||||
To = to;
|
||||
|
@ -51,17 +61,19 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Reset() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Reset current branch to {To.SHA} ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Reset(_repo.FullPath, To.SHA, SelectedMode.Arg).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +1,42 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Revert : Popup {
|
||||
public Models.Commit Target {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Revert : Popup
|
||||
{
|
||||
public Models.Commit Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool AutoCommit {
|
||||
public bool AutoCommit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Revert(Repository repo, Models.Commit target) {
|
||||
public Revert(Repository repo, Models.Commit target)
|
||||
{
|
||||
_repo = repo;
|
||||
Target = target;
|
||||
AutoCommit = true;
|
||||
View = new Views.Revert() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Revert commit '{Target.SHA}' ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Revert(_repo.FullPath, Target.SHA, AutoCommit).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +1,55 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class RevisionCompare : ObservableObject {
|
||||
public Models.Commit StartPoint {
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class RevisionCompare : ObservableObject
|
||||
{
|
||||
public Models.Commit StartPoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Commit EndPoint {
|
||||
public Models.Commit EndPoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public List<Models.Change> VisibleChanges {
|
||||
public List<Models.Change> VisibleChanges
|
||||
{
|
||||
get => _visibleChanges;
|
||||
private set => SetProperty(ref _visibleChanges, value);
|
||||
}
|
||||
|
||||
public List<FileTreeNode> ChangeTree {
|
||||
public List<FileTreeNode> ChangeTree
|
||||
{
|
||||
get => _changeTree;
|
||||
private set => SetProperty(ref _changeTree, value);
|
||||
}
|
||||
|
||||
public Models.Change SelectedChange {
|
||||
public Models.Change SelectedChange
|
||||
{
|
||||
get => _selectedChange;
|
||||
set {
|
||||
if (SetProperty(ref _selectedChange, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedChange, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedNode = null;
|
||||
DiffContext = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, EndPoint.SHA, value));
|
||||
}
|
||||
|
@ -43,60 +57,77 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public FileTreeNode SelectedNode {
|
||||
public FileTreeNode SelectedNode
|
||||
{
|
||||
get => _selectedNode;
|
||||
set {
|
||||
if (SetProperty(ref _selectedNode, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedNode, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedChange = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedChange = value.Backend as Models.Change;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SearchFilter {
|
||||
public string SearchFilter
|
||||
{
|
||||
get => _searchFilter;
|
||||
set {
|
||||
if (SetProperty(ref _searchFilter, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchFilter, value))
|
||||
{
|
||||
RefreshVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiffContext DiffContext {
|
||||
public DiffContext DiffContext
|
||||
{
|
||||
get => _diffContext;
|
||||
private set => SetProperty(ref _diffContext, value);
|
||||
}
|
||||
|
||||
public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint) {
|
||||
public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint)
|
||||
{
|
||||
_repo = repo;
|
||||
StartPoint = startPoint;
|
||||
EndPoint = endPoint;
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
_changes = new Commands.CompareRevisions(_repo, startPoint.SHA, endPoint.SHA).Result();
|
||||
|
||||
var visible = _changes;
|
||||
if (!string.IsNullOrWhiteSpace(_searchFilter)) {
|
||||
if (!string.IsNullOrWhiteSpace(_searchFilter))
|
||||
{
|
||||
visible = new List<Models.Change>();
|
||||
foreach (var c in _changes) {
|
||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var c in _changes)
|
||||
{
|
||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tree = FileTreeNode.Build(visible);
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
VisibleChanges = visible;
|
||||
ChangeTree = tree;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
public void Cleanup()
|
||||
{
|
||||
_repo = null;
|
||||
if (_changes != null) _changes.Clear();
|
||||
if (_visibleChanges != null) _visibleChanges.Clear();
|
||||
|
@ -107,22 +138,27 @@ namespace SourceGit.ViewModels {
|
|||
_diffContext = null;
|
||||
}
|
||||
|
||||
public void NavigateTo(string commitSHA) {
|
||||
public void NavigateTo(string commitSHA)
|
||||
{
|
||||
var repo = Preference.FindRepository(_repo);
|
||||
if (repo != null) repo.NavigateToCommit(commitSHA);
|
||||
}
|
||||
|
||||
public void ClearSearchFilter() {
|
||||
public void ClearSearchFilter()
|
||||
{
|
||||
SearchFilter = string.Empty;
|
||||
}
|
||||
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change) {
|
||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
|
||||
if (change.Index != Models.ChangeState.Deleted) {
|
||||
if (change.Index != Models.ChangeState.Deleted)
|
||||
{
|
||||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Click += (_, ev) => {
|
||||
history.Click += (_, ev) =>
|
||||
{
|
||||
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) };
|
||||
window.Show();
|
||||
ev.Handled = true;
|
||||
|
@ -132,7 +168,8 @@ namespace SourceGit.ViewModels {
|
|||
var explore = new MenuItem();
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.IsEnabled = File.Exists(full);
|
||||
explore.Click += (_, ev) => {
|
||||
explore.Click += (_, ev) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(full, true);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -143,7 +180,8 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Click += (_, ev) => {
|
||||
copyPath.Click += (_, ev) =>
|
||||
{
|
||||
App.CopyText(change.Path);
|
||||
ev.Handled = true;
|
||||
};
|
||||
|
@ -152,15 +190,21 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
private void RefreshVisible() {
|
||||
private void RefreshVisible()
|
||||
{
|
||||
if (_changes == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(_searchFilter)) {
|
||||
if (string.IsNullOrEmpty(_searchFilter))
|
||||
{
|
||||
VisibleChanges = _changes;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var visible = new List<Models.Change>();
|
||||
foreach (var c in _changes) {
|
||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
foreach (var c in _changes)
|
||||
{
|
||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
visible.Add(c);
|
||||
}
|
||||
}
|
||||
|
@ -180,4 +224,4 @@ namespace SourceGit.ViewModels {
|
|||
private string _searchFilter = string.Empty;
|
||||
private DiffContext _diffContext = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +1,47 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Reword : Popup {
|
||||
public Models.Commit Head {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Reword : Popup
|
||||
{
|
||||
public Models.Commit Head
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Commit message is required!!!")]
|
||||
public string Message {
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value, true);
|
||||
}
|
||||
|
||||
public Reword(Repository repo, Models.Commit head) {
|
||||
public Reword(Repository repo, Models.Commit head)
|
||||
{
|
||||
_repo = repo;
|
||||
Head = head;
|
||||
Message = head.FullMessage;
|
||||
View = new Views.Reword() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
if (_message == Head.FullMessage) return null;
|
||||
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Editing head commit message ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Commit(_repo.FullPath, _message, true, true).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return succ;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _message = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,31 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Squash : Popup {
|
||||
public Models.Commit Head {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Squash : Popup
|
||||
{
|
||||
public Models.Commit Head
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Models.Commit Parent {
|
||||
public Models.Commit Parent
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Required(ErrorMessage = "Commit message is required!!!")]
|
||||
public string Message {
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value, true);
|
||||
}
|
||||
|
||||
public Squash(Repository repo, Models.Commit head, Models.Commit parent) {
|
||||
public Squash(Repository repo, Models.Commit head, Models.Commit parent)
|
||||
{
|
||||
_repo = repo;
|
||||
_message = parent.FullMessage;
|
||||
Head = head;
|
||||
|
@ -27,11 +33,13 @@ namespace SourceGit.ViewModels {
|
|||
View = new Views.Squash() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = "Squashing ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var succ = new Commands.Reset(_repo.FullPath, Parent.SHA, "--soft").Exec();
|
||||
if (succ) succ = new Commands.Commit(_repo.FullPath, _message, true).Exec();
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
|
@ -39,7 +47,7 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private readonly Repository _repo = null;
|
||||
private string _message = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class StashChanges : Popup {
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class StashChanges : Popup
|
||||
{
|
||||
|
||||
public string Message {
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool CanIgnoreUntracked {
|
||||
public bool CanIgnoreUntracked
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IncludeUntracked {
|
||||
public bool IncludeUntracked
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public StashChanges(Repository repo, List<Models.Change> changes, bool canIgnoreUntracked) {
|
||||
public StashChanges(Repository repo, List<Models.Change> changes, bool canIgnoreUntracked)
|
||||
{
|
||||
_repo = repo;
|
||||
_changes = changes;
|
||||
|
||||
|
||||
CanIgnoreUntracked = canIgnoreUntracked;
|
||||
IncludeUntracked = true;
|
||||
View = new Views.StashChanges() { DataContext = this };
|
||||
}
|
||||
|
||||
public override Task<bool> Sure() {
|
||||
public override Task<bool> Sure()
|
||||
{
|
||||
var jobs = _changes;
|
||||
if (CanIgnoreUntracked && !IncludeUntracked) {
|
||||
if (CanIgnoreUntracked && !IncludeUntracked)
|
||||
{
|
||||
jobs = new List<Models.Change>();
|
||||
foreach (var job in _changes) {
|
||||
if (job.WorkTree != Models.ChangeState.Untracked && job.WorkTree != Models.ChangeState.Added) {
|
||||
foreach (var job in _changes)
|
||||
{
|
||||
if (job.WorkTree != Models.ChangeState.Untracked && job.WorkTree != Models.ChangeState.Added)
|
||||
{
|
||||
jobs.Add(job);
|
||||
}
|
||||
}
|
||||
|
@ -44,14 +54,15 @@ namespace SourceGit.ViewModels {
|
|||
_repo.SetWatcherEnabled(false);
|
||||
ProgressDescription = $"Stash changes ...";
|
||||
|
||||
return Task.Run(() => {
|
||||
return Task.Run(() =>
|
||||
{
|
||||
new Commands.Stash(_repo.FullPath).Push(jobs, Message);
|
||||
CallUIThread(() => _repo.SetWatcherEnabled(true));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private List<Models.Change> _changes = null;
|
||||
private readonly Repository _repo = null;
|
||||
private readonly List<Models.Change> _changes = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +1,49 @@
|
|||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class StashesPage : ObservableObject {
|
||||
public int Count {
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class StashesPage : ObservableObject
|
||||
{
|
||||
public int Count
|
||||
{
|
||||
get => _stashes == null ? 0 : _stashes.Count;
|
||||
}
|
||||
|
||||
public List<Models.Stash> Stashes {
|
||||
public List<Models.Stash> Stashes
|
||||
{
|
||||
get => _stashes;
|
||||
set {
|
||||
if (SetProperty(ref _stashes, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _stashes, value))
|
||||
{
|
||||
SelectedStash = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Models.Stash SelectedStash {
|
||||
public Models.Stash SelectedStash
|
||||
{
|
||||
get => _selectedStash;
|
||||
set {
|
||||
if (SetProperty(ref _selectedStash, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedStash, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Changes = null;
|
||||
} else {
|
||||
Task.Run(() => {
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var changes = new Commands.QueryStashChanges(_repo.FullPath, value.SHA).Result();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
Changes = changes;
|
||||
});
|
||||
});
|
||||
|
@ -36,38 +52,50 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Models.Change> Changes {
|
||||
public List<Models.Change> Changes
|
||||
{
|
||||
get => _changes;
|
||||
private set {
|
||||
if (SetProperty(ref _changes, value)) {
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _changes, value))
|
||||
{
|
||||
SelectedChange = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Models.Change SelectedChange {
|
||||
public Models.Change SelectedChange
|
||||
{
|
||||
get => _selectedChange;
|
||||
set {
|
||||
if (SetProperty(ref _selectedChange, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedChange, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
DiffContext = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption($"{_selectedStash.SHA}^", _selectedStash.SHA, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DiffContext DiffContext {
|
||||
public DiffContext DiffContext
|
||||
{
|
||||
get => _diffContext;
|
||||
private set => SetProperty(ref _diffContext, value);
|
||||
}
|
||||
|
||||
public StashesPage(Repository repo) {
|
||||
public StashesPage(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
public void Cleanup()
|
||||
{
|
||||
_repo = null;
|
||||
if (_stashes != null) _stashes.Clear();
|
||||
_selectedStash = null;
|
||||
|
@ -76,30 +104,40 @@ namespace SourceGit.ViewModels {
|
|||
_diffContext = null;
|
||||
}
|
||||
|
||||
public void Apply(object param) {
|
||||
if (param is Models.Stash stash) {
|
||||
Task.Run(() => {
|
||||
public void Apply(object param)
|
||||
{
|
||||
if (param is Models.Stash stash)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
new Commands.Stash(_repo.FullPath).Apply(stash.Name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Pop(object param) {
|
||||
if (param is Models.Stash stash) {
|
||||
Task.Run(() => {
|
||||
public void Pop(object param)
|
||||
{
|
||||
if (param is Models.Stash stash)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
new Commands.Stash(_repo.FullPath).Pop(stash.Name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Drop(object param) {
|
||||
if (param is Models.Stash stash && PopupHost.CanCreatePopup()) {
|
||||
public void Drop(object param)
|
||||
{
|
||||
if (param is Models.Stash stash && PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
public void Clear()
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new ClearStashes(_repo));
|
||||
}
|
||||
}
|
||||
|
@ -111,4 +149,4 @@ namespace SourceGit.ViewModels {
|
|||
private Models.Change _selectedChange = null;
|
||||
private DiffContext _diffContext = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +1,43 @@
|
|||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Statistics : ObservableObject {
|
||||
public bool IsLoading {
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Statistics : ObservableObject
|
||||
{
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
private set => SetProperty(ref _isLoading, value);
|
||||
}
|
||||
|
||||
public int SelectedIndex {
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => _selectedIndex;
|
||||
set {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedIndex, value)) RefreshReport();
|
||||
}
|
||||
}
|
||||
|
||||
public Models.StatisticsReport SelectedReport {
|
||||
|
||||
public Models.StatisticsReport SelectedReport
|
||||
{
|
||||
get => _selectedReport;
|
||||
private set => SetProperty(ref _selectedReport, value);
|
||||
}
|
||||
|
||||
public Statistics(string repo) {
|
||||
public Statistics(string repo)
|
||||
{
|
||||
_repo = repo;
|
||||
|
||||
Task.Run(() => {
|
||||
Task.Run(() =>
|
||||
{
|
||||
var result = new Commands.Statistics(_repo).Result();
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
_data = result;
|
||||
RefreshReport();
|
||||
IsLoading = false;
|
||||
|
@ -34,20 +45,22 @@ namespace SourceGit.ViewModels {
|
|||
});
|
||||
}
|
||||
|
||||
private void RefreshReport() {
|
||||
private void RefreshReport()
|
||||
{
|
||||
if (_data == null) return;
|
||||
|
||||
switch (_selectedIndex) {
|
||||
case 0: SelectedReport = _data.Year; break;
|
||||
case 1: SelectedReport = _data.Month; break;
|
||||
default: SelectedReport = _data.Week; break;
|
||||
switch (_selectedIndex)
|
||||
{
|
||||
case 0: SelectedReport = _data.Year; break;
|
||||
case 1: SelectedReport = _data.Month; break;
|
||||
default: SelectedReport = _data.Week; break;
|
||||
}
|
||||
}
|
||||
|
||||
private string _repo = string.Empty;
|
||||
private readonly string _repo = string.Empty;
|
||||
private bool _isLoading = true;
|
||||
private Models.Statistics _data = null;
|
||||
private Models.StatisticsReport _selectedReport = null;
|
||||
private int _selectedIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +1,54 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class TwoSideTextDiff : ObservableObject {
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class TwoSideTextDiff : ObservableObject
|
||||
{
|
||||
public string File { get; set; } = string.Empty;
|
||||
public List<Models.TextDiffLine> Old { get; set; } = new List<Models.TextDiffLine>();
|
||||
public List<Models.TextDiffLine> New { get; set; } = new List<Models.TextDiffLine>();
|
||||
public int MaxLineNumber = 0;
|
||||
|
||||
public TwoSideTextDiff(Models.TextDiff diff) {
|
||||
public TwoSideTextDiff(Models.TextDiff diff)
|
||||
{
|
||||
File = diff.File;
|
||||
MaxLineNumber = diff.MaxLineNumber;
|
||||
|
||||
foreach (var line in diff.Lines) {
|
||||
switch (line.Type) {
|
||||
case Models.TextDiffLineType.Added:
|
||||
New.Add(line);
|
||||
break;
|
||||
case Models.TextDiffLineType.Deleted:
|
||||
Old.Add(line);
|
||||
break;
|
||||
default:
|
||||
FillEmptyLines();
|
||||
Old.Add(line);
|
||||
New.Add(line);
|
||||
break;
|
||||
foreach (var line in diff.Lines)
|
||||
{
|
||||
switch (line.Type)
|
||||
{
|
||||
case Models.TextDiffLineType.Added:
|
||||
New.Add(line);
|
||||
break;
|
||||
case Models.TextDiffLineType.Deleted:
|
||||
Old.Add(line);
|
||||
break;
|
||||
default:
|
||||
FillEmptyLines();
|
||||
Old.Add(line);
|
||||
New.Add(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FillEmptyLines();
|
||||
}
|
||||
|
||||
private void FillEmptyLines() {
|
||||
if (Old.Count < New.Count) {
|
||||
private void FillEmptyLines()
|
||||
{
|
||||
if (Old.Count < New.Count)
|
||||
{
|
||||
int diff = New.Count - Old.Count;
|
||||
for (int i = 0; i < diff; i++) Old.Add(new Models.TextDiffLine());
|
||||
} else if (Old.Count > New.Count) {
|
||||
}
|
||||
else if (Old.Count > New.Count)
|
||||
{
|
||||
int diff = Old.Count - New.Count;
|
||||
for (int i = 0; i < diff; i++) New.Add(new Models.TextDiffLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,103 +1,138 @@
|
|||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class Welcome : ObservableObject {
|
||||
public bool IsClearSearchVisible {
|
||||
using Avalonia.Collections;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class Welcome : ObservableObject
|
||||
{
|
||||
public bool IsClearSearchVisible
|
||||
{
|
||||
get => !string.IsNullOrEmpty(_searchFilter);
|
||||
}
|
||||
|
||||
public AvaloniaList<RepositoryNode> RepositoryNodes {
|
||||
public AvaloniaList<RepositoryNode> RepositoryNodes
|
||||
{
|
||||
get => Preference.Instance.RepositoryNodes;
|
||||
}
|
||||
|
||||
public string SearchFilter {
|
||||
public string SearchFilter
|
||||
{
|
||||
get => _searchFilter;
|
||||
set {
|
||||
if (SetProperty(ref _searchFilter, value)) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchFilter, value))
|
||||
{
|
||||
Referesh();
|
||||
OnPropertyChanged(nameof(IsClearSearchVisible));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InitRepository(string path) {
|
||||
if (!Preference.Instance.IsGitConfigured) {
|
||||
public void InitRepository(string path)
|
||||
{
|
||||
if (!Preference.Instance.IsGitConfigured)
|
||||
{
|
||||
App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new Init(path));
|
||||
}
|
||||
}
|
||||
|
||||
public void Clone(object param) {
|
||||
public void Clone(object param)
|
||||
{
|
||||
var launcher = param as Launcher;
|
||||
var page = launcher.ActivePage;
|
||||
|
||||
if (!Preference.Instance.IsGitConfigured) {
|
||||
if (!Preference.Instance.IsGitConfigured)
|
||||
{
|
||||
App.RaiseException(page.GetId(), App.Text("NotConfigured"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new Clone(launcher, page));
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenTerminal() {
|
||||
if (!Preference.Instance.IsGitConfigured) {
|
||||
public void OpenTerminal()
|
||||
{
|
||||
if (!Preference.Instance.IsGitConfigured)
|
||||
{
|
||||
App.RaiseException(PopupHost.Active.GetId(), App.Text("NotConfigured"));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Native.OS.OpenTerminal(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSearchFilter() {
|
||||
public void ClearSearchFilter()
|
||||
{
|
||||
SearchFilter = string.Empty;
|
||||
}
|
||||
|
||||
public void AddFolder() {
|
||||
public void AddFolder()
|
||||
{
|
||||
if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateGroup(null));
|
||||
}
|
||||
|
||||
public void MoveNode(RepositoryNode from, RepositoryNode to) {
|
||||
public void MoveNode(RepositoryNode from, RepositoryNode to)
|
||||
{
|
||||
Preference.MoveNode(from, to);
|
||||
}
|
||||
|
||||
private void Referesh() {
|
||||
if (string.IsNullOrWhiteSpace(_searchFilter)) {
|
||||
private void Referesh()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_searchFilter))
|
||||
{
|
||||
foreach (var node in RepositoryNodes) ResetVisibility(node);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var node in RepositoryNodes) SetVisibilityBySearch(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetVisibility(RepositoryNode node) {
|
||||
private void ResetVisibility(RepositoryNode node)
|
||||
{
|
||||
node.IsVisible = true;
|
||||
foreach (var subNode in node.SubNodes) ResetVisibility(subNode);
|
||||
}
|
||||
|
||||
private void SetVisibilityBySearch(RepositoryNode node) {
|
||||
if (!node.IsRepository) {
|
||||
if (node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) {
|
||||
private void SetVisibilityBySearch(RepositoryNode node)
|
||||
{
|
||||
if (!node.IsRepository)
|
||||
{
|
||||
if (node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
node.IsVisible = true;
|
||||
foreach (var subNode in node.SubNodes) ResetVisibility(subNode);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasVisibleSubNode = false;
|
||||
foreach (var subNode in node.SubNodes) {
|
||||
foreach (var subNode in node.SubNodes)
|
||||
{
|
||||
SetVisibilityBySearch(subNode);
|
||||
hasVisibleSubNode |= subNode.IsVisible;
|
||||
}
|
||||
node.IsVisible = hasVisibleSubNode;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
node.IsVisible = node.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private string _searchFilter = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +1,72 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SourceGit.ViewModels {
|
||||
public class ConflictContext {
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class ConflictContext
|
||||
{
|
||||
public Models.Change Change { get; set; }
|
||||
}
|
||||
|
||||
public class WorkingCopy : ObservableObject {
|
||||
public bool IsStaging {
|
||||
public class WorkingCopy : ObservableObject
|
||||
{
|
||||
public bool IsStaging
|
||||
{
|
||||
get => _isStaging;
|
||||
private set => SetProperty(ref _isStaging, value);
|
||||
}
|
||||
|
||||
public bool IsUnstaging {
|
||||
public bool IsUnstaging
|
||||
{
|
||||
get => _isUnstaging;
|
||||
private set => SetProperty(ref _isUnstaging, value);
|
||||
}
|
||||
|
||||
public bool IsCommitting {
|
||||
get => _isCommitting;
|
||||
public bool IsCommitting
|
||||
{
|
||||
get => _isCommitting;
|
||||
private set => SetProperty(ref _isCommitting, value);
|
||||
}
|
||||
|
||||
public bool UseAmend {
|
||||
public bool UseAmend
|
||||
{
|
||||
get => _useAmend;
|
||||
set => SetProperty(ref _useAmend, value);
|
||||
}
|
||||
|
||||
public List<Models.Change> Unstaged {
|
||||
public List<Models.Change> Unstaged
|
||||
{
|
||||
get => _unstaged;
|
||||
private set => SetProperty(ref _unstaged, value);
|
||||
}
|
||||
|
||||
public List<Models.Change> Staged {
|
||||
public List<Models.Change> Staged
|
||||
{
|
||||
get => _staged;
|
||||
private set => SetProperty(ref _staged, value);
|
||||
}
|
||||
|
||||
public int Count {
|
||||
public int Count
|
||||
{
|
||||
get => _count;
|
||||
}
|
||||
|
||||
public Models.Change SelectedUnstagedChange {
|
||||
public Models.Change SelectedUnstagedChange
|
||||
{
|
||||
get => _selectedUnstagedChange;
|
||||
set {
|
||||
if (SetProperty(ref _selectedUnstagedChange, value) && value != null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedUnstagedChange, value) && value != null)
|
||||
{
|
||||
SelectedStagedChange = null;
|
||||
SelectedStagedTreeNode = null;
|
||||
SetDetail(value, true);
|
||||
|
@ -59,10 +74,13 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public Models.Change SelectedStagedChange {
|
||||
public Models.Change SelectedStagedChange
|
||||
{
|
||||
get => _selectedStagedChange;
|
||||
set {
|
||||
if (SetProperty(ref _selectedStagedChange, value) && value != null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedStagedChange, value) && value != null)
|
||||
{
|
||||
SelectedUnstagedChange = null;
|
||||
SelectedUnstagedTreeNode = null;
|
||||
SetDetail(value, false);
|
||||
|
@ -70,28 +88,37 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public List<FileTreeNode> UnstagedTree {
|
||||
get => _unstagedTree;
|
||||
public List<FileTreeNode> UnstagedTree
|
||||
{
|
||||
get => _unstagedTree;
|
||||
private set => SetProperty(ref _unstagedTree, value);
|
||||
}
|
||||
|
||||
public List<FileTreeNode> StagedTree {
|
||||
public List<FileTreeNode> StagedTree
|
||||
{
|
||||
get => _stagedTree;
|
||||
private set => SetProperty(ref _stagedTree, value);
|
||||
}
|
||||
|
||||
public FileTreeNode SelectedUnstagedTreeNode {
|
||||
public FileTreeNode SelectedUnstagedTreeNode
|
||||
{
|
||||
get => _selectedUnstagedTreeNode;
|
||||
set {
|
||||
if (SetProperty(ref _selectedUnstagedTreeNode, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedUnstagedTreeNode, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedUnstagedChange = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedUnstagedChange = value.Backend as Models.Change;
|
||||
SelectedStagedTreeNode = null;
|
||||
SelectedStagedChange = null;
|
||||
|
||||
if (value.IsFolder) {
|
||||
if (value.IsFolder)
|
||||
{
|
||||
SetDetail(null, true);
|
||||
}
|
||||
}
|
||||
|
@ -99,18 +126,25 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public FileTreeNode SelectedStagedTreeNode {
|
||||
public FileTreeNode SelectedStagedTreeNode
|
||||
{
|
||||
get => _selectedStagedTreeNode;
|
||||
set {
|
||||
if (SetProperty(ref _selectedStagedTreeNode, value)) {
|
||||
if (value == null) {
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedStagedTreeNode, value))
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
SelectedStagedChange = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedStagedChange = value.Backend as Models.Change;
|
||||
SelectedUnstagedTreeNode = null;
|
||||
SelectedUnstagedChange = null;
|
||||
|
||||
if (value.IsFolder) {
|
||||
if (value.IsFolder)
|
||||
{
|
||||
SetDetail(null, false);
|
||||
}
|
||||
}
|
||||
|
@ -118,21 +152,25 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public object DetailContext {
|
||||
public object DetailContext
|
||||
{
|
||||
get => _detailContext;
|
||||
private set => SetProperty(ref _detailContext, value);
|
||||
}
|
||||
|
||||
public string CommitMessage {
|
||||
public string CommitMessage
|
||||
{
|
||||
get => _commitMessage;
|
||||
set => SetProperty(ref _commitMessage, value);
|
||||
}
|
||||
|
||||
public WorkingCopy(Repository repo) {
|
||||
public WorkingCopy(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
public void Cleanup()
|
||||
{
|
||||
_repo = null;
|
||||
if (_unstaged != null) _unstaged.Clear();
|
||||
if (_staged != null) _staged.Clear();
|
||||
|
@ -146,36 +184,45 @@ namespace SourceGit.ViewModels {
|
|||
_commitMessage = string.Empty;
|
||||
}
|
||||
|
||||
public bool SetData(List<Models.Change> changes) {
|
||||
public bool SetData(List<Models.Change> changes)
|
||||
{
|
||||
var unstaged = new List<Models.Change>();
|
||||
var staged = new List<Models.Change>();
|
||||
|
||||
|
||||
var viewFile = string.Empty;
|
||||
var lastSelectedIsUnstaged = false;
|
||||
if (_selectedUnstagedChange != null) {
|
||||
if (_selectedUnstagedChange != null)
|
||||
{
|
||||
viewFile = _selectedUnstagedChange.Path;
|
||||
lastSelectedIsUnstaged = true;
|
||||
} else if (_selectedStagedChange != null) {
|
||||
}
|
||||
else if (_selectedStagedChange != null)
|
||||
{
|
||||
viewFile = _selectedStagedChange.Path;
|
||||
}
|
||||
|
||||
var viewChange = null as Models.Change;
|
||||
var hasConflict = false;
|
||||
foreach (var c in changes) {
|
||||
foreach (var c in changes)
|
||||
{
|
||||
if (c.Index == Models.ChangeState.Modified
|
||||
|| c.Index == Models.ChangeState.Added
|
||||
|| c.Index == Models.ChangeState.Deleted
|
||||
|| c.Index == Models.ChangeState.Renamed) {
|
||||
|| c.Index == Models.ChangeState.Renamed)
|
||||
{
|
||||
staged.Add(c);
|
||||
if (!lastSelectedIsUnstaged && c.Path == viewFile) {
|
||||
if (!lastSelectedIsUnstaged && c.Path == viewFile)
|
||||
{
|
||||
viewChange = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (c.WorkTree != Models.ChangeState.None) {
|
||||
if (c.WorkTree != Models.ChangeState.None)
|
||||
{
|
||||
unstaged.Add(c);
|
||||
hasConflict |= c.IsConflit;
|
||||
if (lastSelectedIsUnstaged && c.Path == viewFile) {
|
||||
if (lastSelectedIsUnstaged && c.Path == viewFile)
|
||||
{
|
||||
viewChange = c;
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +232,8 @@ namespace SourceGit.ViewModels {
|
|||
|
||||
var unstagedTree = FileTreeNode.Build(unstaged);
|
||||
var stagedTree = FileTreeNode.Build(staged);
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
_isLoadingData = true;
|
||||
Unstaged = unstaged;
|
||||
Staged = staged;
|
||||
|
@ -194,20 +242,26 @@ namespace SourceGit.ViewModels {
|
|||
_isLoadingData = false;
|
||||
|
||||
// Restore last selection states.
|
||||
if (viewChange != null) {
|
||||
if (viewChange != null)
|
||||
{
|
||||
var scrollOffset = Vector.Zero;
|
||||
if (_detailContext is DiffContext old) scrollOffset = old.SyncScrollOffset;
|
||||
|
||||
if (lastSelectedIsUnstaged) {
|
||||
if (lastSelectedIsUnstaged)
|
||||
{
|
||||
SelectedUnstagedChange = viewChange;
|
||||
SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedStagedChange = viewChange;
|
||||
SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile);
|
||||
}
|
||||
|
||||
if (_detailContext is DiffContext cur) cur.SyncScrollOffset = scrollOffset;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedUnstagedChange = null;
|
||||
SelectedUnstagedTreeNode = null;
|
||||
SelectedStagedChange = null;
|
||||
|
@ -219,28 +273,39 @@ namespace SourceGit.ViewModels {
|
|||
return hasConflict;
|
||||
}
|
||||
|
||||
public void SetDetail(Models.Change change, bool isUnstaged) {
|
||||
public void SetDetail(Models.Change change, bool isUnstaged)
|
||||
{
|
||||
if (_isLoadingData) return;
|
||||
|
||||
if (change == null) {
|
||||
if (change == null)
|
||||
{
|
||||
DetailContext = null;
|
||||
} else if (change.IsConflit) {
|
||||
}
|
||||
else if (change.IsConflit)
|
||||
{
|
||||
DetailContext = new ConflictContext() { Change = change };
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
||||
}
|
||||
}
|
||||
|
||||
public async void StageChanges(List<Models.Change> changes) {
|
||||
public async void StageChanges(List<Models.Change> changes)
|
||||
{
|
||||
if (_unstaged.Count == 0 || changes.Count == 0) return;
|
||||
|
||||
SetDetail(null, true);
|
||||
IsStaging = true;
|
||||
_repo.SetWatcherEnabled(false);
|
||||
if (changes.Count == _unstaged.Count) {
|
||||
if (changes.Count == _unstaged.Count)
|
||||
{
|
||||
await Task.Run(() => new Commands.Add(_repo.FullPath).Exec());
|
||||
} else {
|
||||
for (int i = 0; i < changes.Count; i += 10) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < changes.Count; i += 10)
|
||||
{
|
||||
var count = Math.Min(10, changes.Count - i);
|
||||
var step = changes.GetRange(i, count);
|
||||
await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec());
|
||||
|
@ -251,16 +316,21 @@ namespace SourceGit.ViewModels {
|
|||
IsStaging = false;
|
||||
}
|
||||
|
||||
public async void UnstageChanges(List<Models.Change> changes) {
|
||||
public async void UnstageChanges(List<Models.Change> changes)
|
||||
{
|
||||
if (_staged.Count == 0 || changes.Count == 0) return;
|
||||
|
||||
SetDetail(null, false);
|
||||
IsUnstaging = true;
|
||||
_repo.SetWatcherEnabled(false);
|
||||
if (changes.Count == _staged.Count) {
|
||||
if (changes.Count == _staged.Count)
|
||||
{
|
||||
await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec());
|
||||
} else {
|
||||
for (int i = 0; i < changes.Count; i += 10) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < changes.Count; i += 10)
|
||||
{
|
||||
var count = Math.Min(10, changes.Count - i);
|
||||
var step = changes.GetRange(i, count);
|
||||
await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Exec());
|
||||
|
@ -271,29 +341,43 @@ namespace SourceGit.ViewModels {
|
|||
IsUnstaging = false;
|
||||
}
|
||||
|
||||
public void Discard(List<Models.Change> changes, bool isUnstaged) {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
if (isUnstaged) {
|
||||
if (changes.Count == _unstaged.Count && _staged.Count == 0) {
|
||||
public void Discard(List<Models.Change> changes, bool isUnstaged)
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
if (isUnstaged)
|
||||
{
|
||||
if (changes.Count == _unstaged.Count && _staged.Count == 0)
|
||||
{
|
||||
PopupHost.ShowPopup(new Discard(_repo));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
PopupHost.ShowPopup(new Discard(_repo, changes, true));
|
||||
}
|
||||
} else {
|
||||
if (changes.Count == _staged.Count && _unstaged.Count == 0) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (changes.Count == _staged.Count && _unstaged.Count == 0)
|
||||
{
|
||||
PopupHost.ShowPopup(new Discard(_repo));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
PopupHost.ShowPopup(new Discard(_repo, changes, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void UseTheirs() {
|
||||
if (_detailContext is ConflictContext ctx) {
|
||||
public async void UseTheirs()
|
||||
{
|
||||
if (_detailContext is ConflictContext ctx)
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).File(ctx.Change.Path, true));
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
await Task.Run(() => new Commands.Add(_repo.FullPath, [ctx.Change]).Exec());
|
||||
}
|
||||
_repo.MarkWorkingCopyDirtyManually();
|
||||
|
@ -301,11 +385,14 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public async void UseMine() {
|
||||
if (_detailContext is ConflictContext ctx) {
|
||||
public async void UseMine()
|
||||
{
|
||||
if (_detailContext is ConflictContext ctx)
|
||||
{
|
||||
_repo.SetWatcherEnabled(false);
|
||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).File(ctx.Change.Path, false));
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
await Task.Run(() => new Commands.Add(_repo.FullPath, [ctx.Change]).Exec());
|
||||
}
|
||||
_repo.MarkWorkingCopyDirtyManually();
|
||||
|
@ -313,13 +400,16 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public async void UseExternalMergeTool() {
|
||||
if (_detailContext is ConflictContext ctx) {
|
||||
public async void UseExternalMergeTool()
|
||||
{
|
||||
if (_detailContext is ConflictContext ctx)
|
||||
{
|
||||
var type = Preference.Instance.ExternalMergeToolType;
|
||||
var exec = Preference.Instance.ExternalMergeToolPath;
|
||||
|
||||
var tool = Models.ExternalMergeTools.Supported.Find(x => x.Type == type);
|
||||
if (tool == null) {
|
||||
if (tool == null)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
||||
return;
|
||||
}
|
||||
|
@ -332,18 +422,22 @@ namespace SourceGit.ViewModels {
|
|||
}
|
||||
}
|
||||
|
||||
public async void DoCommit(bool autoPush) {
|
||||
if (!PopupHost.CanCreatePopup()) {
|
||||
public async void DoCommit(bool autoPush)
|
||||
{
|
||||
if (!PopupHost.CanCreatePopup())
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_staged.Count == 0) {
|
||||
if (_staged.Count == 0)
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_commitMessage)) {
|
||||
if (string.IsNullOrWhiteSpace(_commitMessage))
|
||||
{
|
||||
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
||||
return;
|
||||
}
|
||||
|
@ -354,11 +448,13 @@ namespace SourceGit.ViewModels {
|
|||
IsCommitting = true;
|
||||
_repo.SetWatcherEnabled(false);
|
||||
var succ = await Task.Run(() => new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec());
|
||||
if (succ) {
|
||||
if (succ)
|
||||
{
|
||||
CommitMessage = string.Empty;
|
||||
UseAmend = false;
|
||||
|
||||
if (autoPush) {
|
||||
if (autoPush)
|
||||
{
|
||||
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
||||
}
|
||||
}
|
||||
|
@ -367,11 +463,13 @@ namespace SourceGit.ViewModels {
|
|||
IsCommitting = false;
|
||||
}
|
||||
|
||||
public ContextMenu CreateContextMenuForUnstagedChanges(List<Models.Change> changes) {
|
||||
public ContextMenu CreateContextMenuForUnstagedChanges(List<Models.Change> changes)
|
||||
{
|
||||
if (changes.Count == 0) return null;
|
||||
|
||||
var menu = new ContextMenu();
|
||||
if (changes.Count == 1) {
|
||||
if (changes.Count == 1)
|
||||
{
|
||||
var change = changes[0];
|
||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||
|
||||
|
@ -379,16 +477,18 @@ namespace SourceGit.ViewModels {
|
|||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Click += (_, e) => {
|
||||
explore.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
|
||||
var openWith = new MenuItem();
|
||||
openWith.Header = App.Text("OpenWith");
|
||||
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWith.IsEnabled = File.Exists(path);
|
||||
openWith.Click += (_, e) => {
|
||||
openWith.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenWithDefaultEditor(path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -396,7 +496,8 @@ namespace SourceGit.ViewModels {
|
|||
var stage = new MenuItem();
|
||||
stage.Header = App.Text("FileCM.Stage");
|
||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||
stage.Click += (_, e) => {
|
||||
stage.Click += (_, e) =>
|
||||
{
|
||||
StageChanges(changes);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -404,7 +505,8 @@ namespace SourceGit.ViewModels {
|
|||
var discard = new MenuItem();
|
||||
discard.Header = App.Text("FileCM.Discard");
|
||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
discard.Click += (_, e) => {
|
||||
discard.Click += (_, e) =>
|
||||
{
|
||||
Discard(changes, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -412,8 +514,10 @@ namespace SourceGit.ViewModels {
|
|||
var stash = new MenuItem();
|
||||
stash.Header = App.Text("FileCM.Stash");
|
||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||
stash.Click += (_, e) => {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
stash.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
||||
}
|
||||
e.Handled = true;
|
||||
|
@ -422,7 +526,8 @@ namespace SourceGit.ViewModels {
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) => {
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
|
@ -432,7 +537,8 @@ namespace SourceGit.ViewModels {
|
|||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null) {
|
||||
if (storageFile != null)
|
||||
{
|
||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
||||
if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
@ -443,7 +549,8 @@ namespace SourceGit.ViewModels {
|
|||
var history = new MenuItem();
|
||||
history.Header = App.Text("FileHistory");
|
||||
history.Icon = App.CreateMenuIcon("Icons.Histories");
|
||||
history.Click += (_, e) => {
|
||||
history.Click += (_, e) =>
|
||||
{
|
||||
var window = new Views.FileHistories() { DataContext = new FileHistories(_repo.FullPath, change.Path) };
|
||||
window.Show();
|
||||
e.Handled = true;
|
||||
|
@ -453,7 +560,8 @@ namespace SourceGit.ViewModels {
|
|||
assumeUnchanged.Header = App.Text("FileCM.AssumeUnchanged");
|
||||
assumeUnchanged.Icon = App.CreateMenuIcon("Icons.File.Ignore");
|
||||
assumeUnchanged.IsEnabled = change.WorkTree != Models.ChangeState.Untracked;
|
||||
assumeUnchanged.Click += (_, e) => {
|
||||
assumeUnchanged.Click += (_, e) =>
|
||||
{
|
||||
new Commands.AssumeUnchanged(_repo.FullPath).Add(change.Path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -461,7 +569,8 @@ namespace SourceGit.ViewModels {
|
|||
var copy = new MenuItem();
|
||||
copy.Header = App.Text("CopyPath");
|
||||
copy.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copy.Click += (_, e) => {
|
||||
copy.Click += (_, e) =>
|
||||
{
|
||||
App.CopyText(change.Path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -478,11 +587,14 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(assumeUnchanged);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copy);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var stage = new MenuItem();
|
||||
stage.Header = App.Text("FileCM.StageMulti", changes.Count);
|
||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||
stage.Click += (_, e) => {
|
||||
stage.Click += (_, e) =>
|
||||
{
|
||||
StageChanges(changes);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -490,7 +602,8 @@ namespace SourceGit.ViewModels {
|
|||
var discard = new MenuItem();
|
||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
discard.Click += (_, e) => {
|
||||
discard.Click += (_, e) =>
|
||||
{
|
||||
Discard(changes, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -498,8 +611,10 @@ namespace SourceGit.ViewModels {
|
|||
var stash = new MenuItem();
|
||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||
stash.Click += (_, e) => {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
stash.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
||||
}
|
||||
e.Handled = true;
|
||||
|
@ -508,7 +623,8 @@ namespace SourceGit.ViewModels {
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (o, e) => {
|
||||
patch.Click += async (o, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
|
@ -516,9 +632,10 @@ namespace SourceGit.ViewModels {
|
|||
options.Title = App.Text("FileCM.SaveAsPatch");
|
||||
options.DefaultExtension = ".patch";
|
||||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
|
||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null) {
|
||||
if (storageFile != null)
|
||||
{
|
||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
||||
if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
@ -530,16 +647,18 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(discard);
|
||||
menu.Items.Add(stash);
|
||||
menu.Items.Add(patch);
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateContextMenuForStagedChanges(List<Models.Change> changes) {
|
||||
public ContextMenu CreateContextMenuForStagedChanges(List<Models.Change> changes)
|
||||
{
|
||||
if (changes.Count == 0) return null;
|
||||
|
||||
var menu = new ContextMenu();
|
||||
if (changes.Count == 1) {
|
||||
if (changes.Count == 1)
|
||||
{
|
||||
var change = changes[0];
|
||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||
|
||||
|
@ -547,7 +666,8 @@ namespace SourceGit.ViewModels {
|
|||
explore.IsEnabled = File.Exists(path) || Directory.Exists(path);
|
||||
explore.Header = App.Text("RevealFile");
|
||||
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
|
||||
explore.Click += (o, e) => {
|
||||
explore.Click += (o, e) =>
|
||||
{
|
||||
Native.OS.OpenInFileManager(path, true);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -556,7 +676,8 @@ namespace SourceGit.ViewModels {
|
|||
openWith.Header = App.Text("OpenWith");
|
||||
openWith.Icon = App.CreateMenuIcon("Icons.OpenWith");
|
||||
openWith.IsEnabled = File.Exists(path);
|
||||
openWith.Click += (_, e) => {
|
||||
openWith.Click += (_, e) =>
|
||||
{
|
||||
Native.OS.OpenWithDefaultEditor(path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -564,7 +685,8 @@ namespace SourceGit.ViewModels {
|
|||
var unstage = new MenuItem();
|
||||
unstage.Header = App.Text("FileCM.Unstage");
|
||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||
unstage.Click += (o, e) => {
|
||||
unstage.Click += (o, e) =>
|
||||
{
|
||||
UnstageChanges(changes);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -572,7 +694,8 @@ namespace SourceGit.ViewModels {
|
|||
var discard = new MenuItem();
|
||||
discard.Header = App.Text("FileCM.Discard");
|
||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
discard.Click += (_, e) => {
|
||||
discard.Click += (_, e) =>
|
||||
{
|
||||
Discard(changes, false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -580,8 +703,10 @@ namespace SourceGit.ViewModels {
|
|||
var stash = new MenuItem();
|
||||
stash.Header = App.Text("FileCM.Stash");
|
||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||
stash.Click += (_, e) => {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
stash.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
||||
}
|
||||
e.Handled = true;
|
||||
|
@ -590,7 +715,8 @@ namespace SourceGit.ViewModels {
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (o, e) => {
|
||||
patch.Click += async (o, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
|
@ -600,7 +726,8 @@ namespace SourceGit.ViewModels {
|
|||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null) {
|
||||
if (storageFile != null)
|
||||
{
|
||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
||||
if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
@ -611,7 +738,8 @@ namespace SourceGit.ViewModels {
|
|||
var copyPath = new MenuItem();
|
||||
copyPath.Header = App.Text("CopyPath");
|
||||
copyPath.Icon = App.CreateMenuIcon("Icons.Copy");
|
||||
copyPath.Click += (o, e) => {
|
||||
copyPath.Click += (o, e) =>
|
||||
{
|
||||
App.CopyText(change.Path);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -625,11 +753,14 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(patch);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
menu.Items.Add(copyPath);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
var unstage = new MenuItem();
|
||||
unstage.Header = App.Text("FileCM.UnstageMulti", changes.Count);
|
||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||
unstage.Click += (o, e) => {
|
||||
unstage.Click += (o, e) =>
|
||||
{
|
||||
UnstageChanges(changes);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -637,7 +768,8 @@ namespace SourceGit.ViewModels {
|
|||
var discard = new MenuItem();
|
||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||
discard.Click += (_, e) => {
|
||||
discard.Click += (_, e) =>
|
||||
{
|
||||
Discard(changes, false);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -645,8 +777,10 @@ namespace SourceGit.ViewModels {
|
|||
var stash = new MenuItem();
|
||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||
stash.Click += (_, e) => {
|
||||
if (PopupHost.CanCreatePopup()) {
|
||||
stash.Click += (_, e) =>
|
||||
{
|
||||
if (PopupHost.CanCreatePopup())
|
||||
{
|
||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
||||
}
|
||||
e.Handled = true;
|
||||
|
@ -655,7 +789,8 @@ namespace SourceGit.ViewModels {
|
|||
var patch = new MenuItem();
|
||||
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||
patch.Click += async (_, e) => {
|
||||
patch.Click += async (_, e) =>
|
||||
{
|
||||
var topLevel = App.GetTopLevel();
|
||||
if (topLevel == null) return;
|
||||
|
||||
|
@ -665,7 +800,8 @@ namespace SourceGit.ViewModels {
|
|||
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||
|
||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||
if (storageFile != null) {
|
||||
if (storageFile != null)
|
||||
{
|
||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
||||
if (succ) App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||
}
|
||||
|
@ -682,9 +818,11 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
public ContextMenu CreateContextMenuForCommitMessages() {
|
||||
public ContextMenu CreateContextMenuForCommitMessages()
|
||||
{
|
||||
var menu = new ContextMenu();
|
||||
if (_repo.CommitMessages.Count == 0) {
|
||||
if (_repo.CommitMessages.Count == 0)
|
||||
{
|
||||
var empty = new MenuItem();
|
||||
empty.Header = App.Text("WorkingCopy.NoCommitHistories");
|
||||
empty.IsEnabled = false;
|
||||
|
@ -698,12 +836,14 @@ namespace SourceGit.ViewModels {
|
|||
menu.Items.Add(tip);
|
||||
menu.Items.Add(new MenuItem() { Header = "-" });
|
||||
|
||||
foreach (var message in _repo.CommitMessages) {
|
||||
foreach (var message in _repo.CommitMessages)
|
||||
{
|
||||
var dump = message;
|
||||
|
||||
var item = new MenuItem();
|
||||
item.Header = dump;
|
||||
item.Click += (o, e) => {
|
||||
item.Click += (o, e) =>
|
||||
{
|
||||
CommitMessage = dump;
|
||||
e.Handled = true;
|
||||
};
|
||||
|
@ -714,16 +854,21 @@ namespace SourceGit.ViewModels {
|
|||
return menu;
|
||||
}
|
||||
|
||||
private void PushCommitMessage() {
|
||||
private void PushCommitMessage()
|
||||
{
|
||||
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
||||
if (existIdx == 0) {
|
||||
if (existIdx == 0)
|
||||
{
|
||||
return;
|
||||
} else if (existIdx > 0) {
|
||||
}
|
||||
else if (existIdx > 0)
|
||||
{
|
||||
_repo.CommitMessages.Move(existIdx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_repo.CommitMessages.Count > 9) {
|
||||
if (_repo.CommitMessages.Count > 9)
|
||||
{
|
||||
_repo.CommitMessages.RemoveRange(9, _repo.CommitMessages.Count - 9);
|
||||
}
|
||||
|
||||
|
@ -748,4 +893,4 @@ namespace SourceGit.ViewModels {
|
|||
private object _detailContext = null;
|
||||
private string _commitMessage = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue