sourcegit/src/Views/Widgets/WorkingCopy.xaml.cs

360 lines
13 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace SourceGit.Views.Widgets {
/// <summary>
/// 工作区
/// </summary>
public partial class WorkingCopy : UserControl {
private Models.Repository repo = null;
private bool isLFSEnabled = false;
public string CommitMessage { get; set; }
public WorkingCopy(Models.Repository repo) {
this.repo = repo;
this.isLFSEnabled = new Commands.LFS(repo.Path).IsEnabled();
InitializeComponent();
unstagedContainer.SetRepository(repo.Path);
stagedContainer.SetRepository(repo.Path);
}
public void SetData(List<Models.Change> changes) {
List<Models.Change> unstagedChanges = new List<Models.Change>();
List<Models.Change> stagedChanges = new List<Models.Change>();
foreach (var c in changes) {
if (c.Index == Models.Change.Status.Modified
|| c.Index == Models.Change.Status.Added
|| c.Index == Models.Change.Status.Deleted
|| c.Index == Models.Change.Status.Renamed) {
stagedChanges.Add(c);
}
if (c.WorkTree != Models.Change.Status.None) {
unstagedChanges.Add(c);
}
}
unstagedContainer.SetData(unstagedChanges);
stagedContainer.SetData(stagedChanges);
var current = repo.Branches.Find(x => x.IsCurrent);
if (current != null && !string.IsNullOrEmpty(current.Upstream) && chkAmend.IsChecked != true) {
btnCommitAndPush.Visibility = Visibility.Visible;
} else {
btnCommitAndPush.Visibility = Visibility.Collapsed;
}
var diffTarget = unstagedContainer.DiffTarget;
if (diffTarget == null) diffTarget = stagedContainer.DiffTarget;
if (diffTarget == null) {
mergePanel.Visibility = Visibility.Collapsed;
diffViewer.Reset();
} else if (diffTarget.IsConflit) {
mergePanel.Visibility = Visibility.Visible;
diffViewer.Reset();
} else {
mergePanel.Visibility = Visibility.Collapsed;
diffViewer.Reload();
}
}
public void TryLoadMergeMessage() {
if (string.IsNullOrEmpty(txtCommitMessage.Text)) {
var mergeMsgFile = Path.Combine(repo.GitDir, "MERGE_MSG");
if (!File.Exists(mergeMsgFile)) return;
var content = File.ReadAllText(mergeMsgFile);
txtCommitMessage.Text = content;
}
}
public void ClearMessage() {
txtCommitMessage.Text = "";
Validation.ClearInvalid(txtCommitMessage.GetBindingExpression(TextBox.TextProperty));
}
#region STAGE_UNSTAGE
private void StageSelected(object sender, RoutedEventArgs e) {
unstagedContainer.StageSelected();
}
private void StageAll(object sender, RoutedEventArgs e) {
unstagedContainer.StageAll();
}
private void UnstageSelected(object sender, RoutedEventArgs e) {
stagedContainer.UnstageSelected();
}
private void UnstageAll(object sender, RoutedEventArgs e) {
stagedContainer.UnstageAll();
}
private void OnDiffTargetChanged(object sender, WorkingCopyChanges.DiffTargetChangedEventArgs e) {
var container = sender as WorkingCopyChanges;
if (container == null) return;
if (e.Target == null) {
if (e.HasOthers) {
if (container.IsUnstaged) {
stagedContainer.UnselectAll();
} else {
unstagedContainer.UnselectAll();
}
mergePanel.Visibility = Visibility.Collapsed;
diffViewer.Reset();
}
return;
}
if (container.IsUnstaged) {
stagedContainer.UnselectAll();
} else {
unstagedContainer.UnselectAll();
}
var change = e.Target;
if (change.IsConflit) {
mergePanel.Visibility = Visibility.Visible;
diffViewer.Reset();
return;
}
mergePanel.Visibility = Visibility.Collapsed;
if (container.IsUnstaged) {
switch (change.WorkTree) {
case Models.Change.Status.Added:
case Models.Change.Status.Untracked:
diffViewer.Diff(repo.Path, new DiffViewer.Option() {
ExtraArgs = "--no-index",
Path = change.Path,
OrgPath = "/dev/null",
UseLFS = isLFSEnabled
});
break;
default:
diffViewer.Diff(repo.Path, new DiffViewer.Option() {
Path = change.Path,
OrgPath = change.OriginalPath,
UseLFS = isLFSEnabled
});
break;
}
} else {
diffViewer.Diff(repo.Path, new DiffViewer.Option() {
ExtraArgs = "--cached",
Path = change.Path,
OrgPath = change.OriginalPath,
UseLFS = isLFSEnabled
});
}
}
#endregion
#region MERGE
private async void UseTheirs(object sender, RoutedEventArgs e) {
var change = unstagedContainer.DiffTarget;
if (change == null || !change.IsConflit) return;
Models.Watcher.SetEnabled(repo.Path, false);
var succ = await Task.Run(() => new Commands.Checkout(repo.Path).File(change.Path, true));
if (succ) {
await Task.Run(() => new Commands.Add(repo.Path, new List<string>() { change.Path }).Exec());
}
Models.Watcher.SetEnabled(repo.Path, true);
e.Handled = true;
}
private async void UseMine(object sender, RoutedEventArgs e) {
var change = unstagedContainer.DiffTarget;
if (change == null || !change.IsConflit) return;
Models.Watcher.SetEnabled(repo.Path, false);
var succ = await Task.Run(() => new Commands.Checkout(repo.Path).File(change.Path, false));
if (succ) {
await Task.Run(() => new Commands.Add(repo.Path, new List<string>() { change.Path }).Exec());
}
Models.Watcher.SetEnabled(repo.Path, true);
e.Handled = true;
}
private async void UseMergeTool(object sender, RoutedEventArgs e) {
var mergeType = Models.Preference.Instance.MergeTool.Type;
var mergeExe = Models.Preference.Instance.MergeTool.Path;
var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType);
if (merger == null || merger.Type == 0 || !File.Exists(mergeExe)) {
Models.Exception.Raise("Invalid merge tool in preference setting!");
return;
}
var change = unstagedContainer.DiffTarget;
if (change == null || !change.IsConflit) return;
var cmd = new Commands.Command();
cmd.Cwd = repo.Path;
cmd.DontRaiseError = true;
cmd.Args = $"-c mergetool.sourcegit.cmd=\"\\\"{mergeExe}\\\" {merger.Cmd}\" ";
cmd.Args += "-c mergetool.writeToTemp=true -c mergetool.keepBackup=false -c mergetool.trustExitCode=true ";
cmd.Args += $"mergetool --tool=sourcegit {change.Path}";
await Task.Run(() => cmd.Exec());
e.Handled = true;
}
#endregion
#region COMMIT
private void OpenCommitMessageRecorder(object sender, RoutedEventArgs e) {
var anchor = sender as Button;
if (anchor.ContextMenu == null) {
anchor.ContextMenu = new ContextMenu();
anchor.ContextMenu.PlacementTarget = anchor;
anchor.ContextMenu.Placement = PlacementMode.Top;
anchor.ContextMenu.VerticalOffset = 0;
anchor.ContextMenu.StaysOpen = false;
anchor.ContextMenu.Focusable = true;
anchor.ContextMenu.MaxWidth = 500;
} else {
anchor.ContextMenu.Items.Clear();
}
if (repo.CommitMessages.Count == 0) {
var tip = new MenuItem();
tip.Header = App.Text("WorkingCopy.NoCommitHistories");
tip.IsEnabled = false;
anchor.ContextMenu.Items.Add(tip);
} else {
var tip = new MenuItem();
tip.Header = App.Text("WorkingCopy.HasCommitHistories");
tip.IsEnabled = false;
anchor.ContextMenu.Items.Add(tip);
anchor.ContextMenu.Items.Add(new Separator());
foreach (var one in repo.CommitMessages) {
var dump = one;
var item = new MenuItem();
item.Header = dump;
item.Padding = new Thickness(0);
item.Click += (o, ev) => {
txtCommitMessage.Text = dump;
ev.Handled = true;
};
anchor.ContextMenu.Items.Add(item);
}
}
anchor.ContextMenu.IsOpen = true;
e.Handled = true;
}
private void StartAmend(object sender, RoutedEventArgs e) {
var commits = new Commands.Commits(repo.Path, "-n 1", false).Result();
if (commits.Count == 0) {
Models.Exception.Raise("No commits to amend!");
chkAmend.IsChecked = false;
return;
}
txtCommitMessage.Text = commits[0].Subject;
btnCommitAndPush.Visibility = Visibility.Collapsed;
e.Handled = true;
}
private void EndAmend(object sender, RoutedEventArgs e) {
if (!IsLoaded) return;
var current = repo.Branches.Find(x => x.IsCurrent);
if (current != null && !string.IsNullOrEmpty(current.Upstream)) {
btnCommitAndPush.Visibility = Visibility.Visible;
} else {
btnCommitAndPush.Visibility = Visibility.Collapsed;
}
e.Handled = true;
}
private async void Commit(object sender, RoutedEventArgs e) {
var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result());
var conflict = changes.Find(x => x.IsConflit);
if (conflict != null) {
Models.Exception.Raise("You have unsolved conflicts in your working copy!");
return;
}
if (stagedContainer.Changes.Count == 0) {
Models.Exception.Raise("No files added to commit!");
return;
}
txtCommitMessage.GetBindingExpression(TextBox.TextProperty).UpdateSource();
if (Validation.GetHasError(txtCommitMessage)) return;
repo.PushCommitMessage(CommitMessage);
iconCommitting.Visibility = Visibility.Visible;
iconCommitting.IsAnimating = true;
Models.Watcher.SetEnabled(repo.Path, false);
var amend = chkAmend.IsChecked == true;
var succ = await Task.Run(() => new Commands.Commit(repo.Path, CommitMessage, amend).Exec());
if (succ) {
ClearMessage();
if (amend) chkAmend.IsChecked = false;
}
iconCommitting.IsAnimating = false;
iconCommitting.Visibility = Visibility.Collapsed;
Models.Watcher.SetEnabled(repo.Path, true);
e.Handled = true;
}
private async void CommitAndPush(object sender, RoutedEventArgs e) {
var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result());
var conflict = changes.Find(x => x.IsConflit);
if (conflict != null) {
Models.Exception.Raise("You have unsolved conflicts in your working copy!");
return;
}
if (stagedContainer.Changes.Count == 0) {
Models.Exception.Raise("No files added to commit!");
return;
}
txtCommitMessage.GetBindingExpression(TextBox.TextProperty).UpdateSource();
if (Validation.GetHasError(txtCommitMessage)) return;
repo.PushCommitMessage(CommitMessage);
iconCommitting.Visibility = Visibility.Visible;
iconCommitting.IsAnimating = true;
Models.Watcher.SetEnabled(repo.Path, false);
var succ = await Task.Run(() => new Commands.Commit(repo.Path, CommitMessage, false).Exec());
if (succ) {
new Popups.Push(repo, repo.Branches.Find(x => x.IsCurrent)).ShowAndStart();
ClearMessage();
}
iconCommitting.IsAnimating = false;
iconCommitting.Visibility = Visibility.Collapsed;
Models.Watcher.SetEnabled(repo.Path, true);
e.Handled = true;
}
#endregion
}
}