feature<WorkingCopy>: gitee issue #I54W26 - toggle untracked files in working copy changes

This commit is contained in:
leo 2022-05-05 09:57:28 +08:00
parent b04c94ccc1
commit 35235df7bc
10 changed files with 3432 additions and 3370 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,253 +1,262 @@
<UserControl x:Class="SourceGit.Views.Widgets.WorkingCopy"
x:Name="me"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
xmlns:converter="clr-namespace:SourceGit.Views.Converters"
xmlns:models="clr-namespace:SourceGit.Models"
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converter:BoolToCollapsed x:Key="BoolToCollapsed"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" MinWidth="300"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Unstaged Toolbar -->
<Border Grid.Row="0" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:ChangeDisplaySwitcher
Grid.Column="0"
x:Name="unstagedMode"
Width="14" Height="14"
Margin="4,0,0,0"
Mode="{Binding Source={x:Static models:Preference.Instance}, Path=Window.ChangeInUnstaged, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Margin="6,0"
Text="{DynamicResource Text.WorkingCopy.Unstaged}"
Foreground="{DynamicResource Brush.FG2}"
FontWeight="Bold"/>
<controls:Loading
Grid.Column="2"
Width="12" Height="12"
x:Name="iconStaging"
IsAnimating="{Binding ElementName=unstagedContainer, Path=IsStaging}"
Visibility="{Binding ElementName=unstagedContainer, Path=IsStaging, Converter={StaticResource BoolToCollapsed}}"/>
<controls:IconButton
Grid.Column="4"
Click="StageSelected"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.Down}"
ToolTip="{DynamicResource Text.WorkingCopy.Unstaged.Stage}"/>
<controls:IconButton
Grid.Column="5"
Click="StageAll"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.DoubleDown}"
ToolTip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}"/>
</Grid>
</Border>
<!-- Unstaged Changes -->
<Border Grid.Row="1" Background="{DynamicResource Brush.Contents}">
<widgets:WorkingCopyChanges
x:Name="unstagedContainer"
IsUnstaged="True"
Mode="{Binding ElementName=unstagedMode, Path=Mode}"
DiffTargetChanged="OnDiffTargetChanged"/>
</Border>
<!-- Staged Toolbar -->
<Border Grid.Row="2" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:ChangeDisplaySwitcher
Grid.Column="0"
x:Name="stagedMode"
Width="14" Height="14"
Margin="4,0,0,0"
Mode="{Binding Source={x:Static models:Preference.Instance}, Path=Window.ChangeInStaged, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Margin="6,0"
Text="{DynamicResource Text.WorkingCopy.Staged}"
Foreground="{DynamicResource Brush.FG2}"
FontWeight="Bold"/>
<controls:IconButton
Grid.Column="2"
Click="UnstageSelected"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.Up}"
ToolTip="{DynamicResource Text.WorkingCopy.Staged.Unstage}"/>
<controls:IconButton
Grid.Column="3"
Click="UnstageAll"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.DoubleUp}"
ToolTip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}"/>
</Grid>
</Border>
<!-- Staged Changes -->
<Border Grid.Row="3" Background="{DynamicResource Brush.Contents}">
<widgets:WorkingCopyChanges
x:Name="stagedContainer"
IsUnstaged="False"
Mode="{Binding ElementName=stagedMode, Path=Mode}"
DiffTargetChanged="OnDiffTargetChanged"/>
</Border>
</Grid>
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Background="{DynamicResource Brush.Border0}"/>
<!-- Right -->
<Grid Grid.Column="2" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Diff viewer -->
<widgets:DiffViewer Grid.Row="0" x:Name="diffViewer"/>
<!-- Merge Option Panel -->
<Grid Grid.Row="0" x:Name="mergePanel" Background="{DynamicResource Brush.Window}" Visibility="Collapsed">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="64" Height="64"
Data="{StaticResource Icon.Conflict}"
Fill="{DynamicResource Brush.FG2}"/>
<TextBlock
Margin="0,16,0,28"
FontSize="20" FontWeight="DemiBold"
Text="{DynamicResource Text.WorkingCopy.Conflicts}"
Foreground="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Click="UseTheirs" Content="{DynamicResource Text.WorkingCopy.UseTheirs}" Height="24" Padding="8,0"/>
<Button Click="UseMine" Content="{DynamicResource Text.WorkingCopy.UseMine}" Height="24" Margin="8,0" Padding="8,0"/>
<Button Click="UseMergeTool" Content="{DynamicResource Text.WorkingCopy.OpenMerger}" Height="24" Padding="8,0"/>
</StackPanel>
</StackPanel>
</Grid>
<!-- Commit Message -->
<controls:TextEdit
Grid.Row="1"
x:Name="txtCommitMessage"
Height="64"
Margin="0,4" Padding="1"
AcceptsReturn="True"
AcceptsTab="True"
TextWrapping="Wrap"
KeyDown="CommitMessageKeyDown"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Placeholder="{DynamicResource Text.WorkingCopy.CommitMessageTip}"
PlaceholderBaseline="Top">
<TextBox.Text>
<Binding ElementName="me" Path="CommitMessage" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validations:CommitMessage/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</controls:TextEdit>
<!-- Commit Options -->
<Grid Grid.Row="2" Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:IconButton
Grid.Column="0"
Width="14" Height="14"
Click="OpenCommitMessageRecorder"
ToolTip="{DynamicResource Text.WorkingCopy.MessageHistories}"
Icon="{StaticResource Icon.List}"
Opacity=".5"/>
<CheckBox
Grid.Column="1"
x:Name="chkAmend"
Margin="8,0,0,0"
HorizontalAlignment="Left"
Content="{DynamicResource Text.WorkingCopy.Amend}"
Checked="StartAmend" Unchecked="EndAmend"/>
<controls:Loading
Grid.Column="3"
x:Name="iconCommitting"
Width="18" Height="18"
Margin="0,0,8,0"
Visibility="Collapsed"/>
<Button
Grid.Column="4"
Height="26"
Padding="8,0"
Click="Commit"
FontWeight="Bold"
Background="{DynamicResource Brush.Accent1}"
BorderBrush="{DynamicResource Brush.FG1}"
Content="{DynamicResource Text.WorkingCopy.Commit}"
ToolTip="{DynamicResource Text.WorkingCopy.CommitTip}"/>
<Button
Grid.Column="5"
x:Name="btnCommitAndPush"
Height="26"
Padding="8,0"
Click="CommitAndPush"
FontWeight="Bold"
Content="{DynamicResource Text.WorkingCopy.CommitAndPush}"
Margin="8,0,0,0"/>
</Grid>
</Grid>
</Grid>
</UserControl>
<UserControl x:Class="SourceGit.Views.Widgets.WorkingCopy"
x:Name="me"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:SourceGit.Views.Controls"
xmlns:converter="clr-namespace:SourceGit.Views.Converters"
xmlns:models="clr-namespace:SourceGit.Models"
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
xmlns:validations="clr-namespace:SourceGit.Views.Validations"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converter:BoolToCollapsed x:Key="BoolToCollapsed"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" MinWidth="300"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Unstaged Toolbar -->
<Border Grid.Row="0" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:ChangeDisplaySwitcher
Grid.Column="0"
x:Name="unstagedMode"
Width="14" Height="14"
Margin="4,0,0,0"
Mode="{Binding Source={x:Static models:Preference.Instance}, Path=Window.ChangeInUnstaged, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Margin="6,0"
Text="{DynamicResource Text.WorkingCopy.Unstaged}"
Foreground="{DynamicResource Brush.FG2}"
FontWeight="Bold"/>
<controls:Loading
Grid.Column="2"
Width="12" Height="12"
x:Name="iconStaging"
IsAnimating="{Binding ElementName=unstagedContainer, Path=IsStaging}"
Visibility="{Binding ElementName=unstagedContainer, Path=IsStaging, Converter={StaticResource BoolToCollapsed}}"/>
<ToggleButton
Grid.Column="4"
Width="14" Height="14"
Margin="4,0"
Style="{StaticResource Style.ToggleButton.Eye}"
ToolTip="{DynamicResource Text.WorkingCopy.IncludeUntracked}"
IsChecked="{Binding Source={x:Static models:Preference.Instance}, Path=Git.IncludeUntrackedInWC, Mode=TwoWay}"
Checked="ToggleIncludeUntracked" Unchecked="ToggleIncludeUntracked"/>
<controls:IconButton
Grid.Column="5"
Click="StageSelected"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.Down}"
ToolTip="{DynamicResource Text.WorkingCopy.Unstaged.Stage}"/>
<controls:IconButton
Grid.Column="6"
Click="StageAll"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.DoubleDown}"
ToolTip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}"/>
</Grid>
</Border>
<!-- Unstaged Changes -->
<Border Grid.Row="1" Background="{DynamicResource Brush.Contents}">
<widgets:WorkingCopyChanges
x:Name="unstagedContainer"
IsUnstaged="True"
Mode="{Binding ElementName=unstagedMode, Path=Mode}"
DiffTargetChanged="OnDiffTargetChanged"/>
</Border>
<!-- Staged Toolbar -->
<Border Grid.Row="2" BorderBrush="{DynamicResource Brush.Border0}" BorderThickness="0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:ChangeDisplaySwitcher
Grid.Column="0"
x:Name="stagedMode"
Width="14" Height="14"
Margin="4,0,0,0"
Mode="{Binding Source={x:Static models:Preference.Instance}, Path=Window.ChangeInStaged, Mode=TwoWay}"/>
<TextBlock
Grid.Column="1"
Margin="6,0"
Text="{DynamicResource Text.WorkingCopy.Staged}"
Foreground="{DynamicResource Brush.FG2}"
FontWeight="Bold"/>
<controls:IconButton
Grid.Column="2"
Click="UnstageSelected"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.Up}"
ToolTip="{DynamicResource Text.WorkingCopy.Staged.Unstage}"/>
<controls:IconButton
Grid.Column="3"
Click="UnstageAll"
Width="14" Height="14"
Margin="4,0"
Icon="{StaticResource Icon.DoubleUp}"
ToolTip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}"/>
</Grid>
</Border>
<!-- Staged Changes -->
<Border Grid.Row="3" Background="{DynamicResource Brush.Contents}">
<widgets:WorkingCopyChanges
x:Name="stagedContainer"
IsUnstaged="False"
Mode="{Binding ElementName=stagedMode, Path=Mode}"
DiffTargetChanged="OnDiffTargetChanged"/>
</Border>
</Grid>
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Background="{DynamicResource Brush.Border0}"/>
<!-- Right -->
<Grid Grid.Column="2" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Diff viewer -->
<widgets:DiffViewer Grid.Row="0" x:Name="diffViewer"/>
<!-- Merge Option Panel -->
<Grid Grid.Row="0" x:Name="mergePanel" Background="{DynamicResource Brush.Window}" Visibility="Collapsed">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path
Width="64" Height="64"
Data="{StaticResource Icon.Conflict}"
Fill="{DynamicResource Brush.FG2}"/>
<TextBlock
Margin="0,16,0,28"
FontSize="20" FontWeight="DemiBold"
Text="{DynamicResource Text.WorkingCopy.Conflicts}"
Foreground="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Click="UseTheirs" Content="{DynamicResource Text.WorkingCopy.UseTheirs}" Height="24" Padding="8,0"/>
<Button Click="UseMine" Content="{DynamicResource Text.WorkingCopy.UseMine}" Height="24" Margin="8,0" Padding="8,0"/>
<Button Click="UseMergeTool" Content="{DynamicResource Text.WorkingCopy.OpenMerger}" Height="24" Padding="8,0"/>
</StackPanel>
</StackPanel>
</Grid>
<!-- Commit Message -->
<controls:TextEdit
Grid.Row="1"
x:Name="txtCommitMessage"
Height="64"
Margin="0,4" Padding="1"
AcceptsReturn="True"
AcceptsTab="True"
TextWrapping="Wrap"
KeyDown="CommitMessageKeyDown"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Placeholder="{DynamicResource Text.WorkingCopy.CommitMessageTip}"
PlaceholderBaseline="Top">
<TextBox.Text>
<Binding ElementName="me" Path="CommitMessage" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validations:CommitMessage/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</controls:TextEdit>
<!-- Commit Options -->
<Grid Grid.Row="2" Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:IconButton
Grid.Column="0"
Width="14" Height="14"
Click="OpenCommitMessageRecorder"
ToolTip="{DynamicResource Text.WorkingCopy.MessageHistories}"
Icon="{StaticResource Icon.List}"
Opacity=".5"/>
<CheckBox
Grid.Column="1"
x:Name="chkAmend"
Margin="8,0,0,0"
HorizontalAlignment="Left"
Content="{DynamicResource Text.WorkingCopy.Amend}"
Checked="StartAmend" Unchecked="EndAmend"/>
<controls:Loading
Grid.Column="3"
x:Name="iconCommitting"
Width="18" Height="18"
Margin="0,0,8,0"
Visibility="Collapsed"/>
<Button
Grid.Column="4"
Height="26"
Padding="8,0"
Click="Commit"
FontWeight="Bold"
Background="{DynamicResource Brush.Accent1}"
BorderBrush="{DynamicResource Brush.FG1}"
Content="{DynamicResource Text.WorkingCopy.Commit}"
ToolTip="{DynamicResource Text.WorkingCopy.CommitTip}"/>
<Button
Grid.Column="5"
x:Name="btnCommitAndPush"
Height="26"
Padding="8,0"
Click="CommitAndPush"
FontWeight="Bold"
Content="{DynamicResource Text.WorkingCopy.CommitAndPush}"
Margin="8,0,0,0"/>
</Grid>
</Grid>
</Grid>
</UserControl>

View file

@ -1,367 +1,372 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
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;
}
private void CommitMessageKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) {
Commit(sender, e);
}
}
#endregion
}
}
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
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));
}
public void ToggleIncludeUntracked(object sender, RoutedEventArgs e) {
var watcher = Models.Watcher.Get(repo.Path);
if (watcher != null) watcher.RefreshWC();
}
#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;
}
private void CommitMessageKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) {
Commit(sender, e);
}
}
#endregion
}
}

File diff suppressed because it is too large Load diff