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 { /// /// 工作区 /// public partial class WorkingCopy : UserControl { private Models.Repository repo = null; public string CommitMessage { get; set; } public WorkingCopy(Models.Repository repo) { this.repo = repo; InitializeComponent(); unstagedContainer.SetRepository(repo.Path); stagedContainer.SetRepository(repo.Path); } public void SetData(List changes) { List unstagedChanges = new List(); List stagedChanges = new List(); 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" }); break; default: diffViewer.Diff(repo.Path, new DiffViewer.Option() { Path = change.Path, OrgPath = change.OriginalPath }); break; } } else { diffViewer.Diff(repo.Path, new DiffViewer.Option() { ExtraArgs = "--cached", Path = change.Path, OrgPath = change.OriginalPath }); } } #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() { 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() { 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.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(); 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 } }