feature<WorkingCopy>: reload diff content when working copy changed

This commit is contained in:
leo 2021-01-05 16:59:35 +08:00
parent d110b9e451
commit 1de529237a
4 changed files with 371 additions and 182 deletions

View file

@ -22,7 +22,9 @@ namespace SourceGit.UI {
public static readonly Brush BG_DELETED = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0)); public static readonly Brush BG_DELETED = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0));
public static readonly Brush BG_NORMAL = Brushes.Transparent; public static readonly Brush BG_NORMAL = Brushes.Transparent;
private List<Git.Diff.LineChange> lineChanges = null; private Git.Repository lastRepo = null;
private Option lastOpts = null;
private List<Git.Diff.LineChange> lastChanges = null;
private List<DataGrid> editors = new List<DataGrid>(); private List<DataGrid> editors = new List<DataGrid>();
/// <summary> /// <summary>
@ -73,9 +75,8 @@ namespace SourceGit.UI {
/// </summary> /// </summary>
public void Reset() { public void Reset() {
mask.Visibility = Visibility.Visible; mask.Visibility = Visibility.Visible;
lineChanges = null; ClearCache();
foreach (var editor in editors) editorContainer.Children.Remove(editor); ClearEditor();
editors.Clear();
} }
/// <summary> /// <summary>
@ -91,11 +92,11 @@ namespace SourceGit.UI {
sizeChange.Visibility = Visibility.Collapsed; sizeChange.Visibility = Visibility.Collapsed;
noChange.Visibility = Visibility.Collapsed; noChange.Visibility = Visibility.Collapsed;
editorContainer.Children.Clear(); ClearEditor();
editorLines.Children.Clear(); ClearCache();
editorLines.ColumnDefinitions.Clear();
editors.Clear(); lastRepo = repo;
lineChanges = null; lastOpts = opts;
Task.Run(() => { Task.Run(() => {
var args = $"{opts.ExtraArgs} "; var args = $"{opts.ExtraArgs} ";
@ -122,7 +123,57 @@ namespace SourceGit.UI {
if (rs.IsBinary) { if (rs.IsBinary) {
SetBinaryChange(Git.Diff.GetSizeChange(repo, opts.RevisionRange, opts.Path, opts.OrgPath)); SetBinaryChange(Git.Diff.GetSizeChange(repo, opts.RevisionRange, opts.Path, opts.OrgPath));
} else if (rs.Lines.Count > 0) { } else if (rs.Lines.Count > 0) {
lineChanges = rs.Lines; lastChanges = rs.Lines;
SetTextChange();
} else {
SetSame();
}
});
}
/// <summary>
/// Reload diff content with last repository and options.
/// </summary>
public void Reload() {
if (lastRepo == null) {
Reset();
return;
}
loading.Visibility = Visibility.Visible;
mask.Visibility = Visibility.Collapsed;
sizeChange.Visibility = Visibility.Collapsed;
noChange.Visibility = Visibility.Collapsed;
var repo = lastRepo;
var opts = lastOpts;
Task.Run(() => {
var args = $"{opts.ExtraArgs} ";
if (opts.RevisionRange.Length > 0) args += $"{opts.RevisionRange[0]} ";
if (opts.RevisionRange.Length > 1) args += $"{opts.RevisionRange[1]} ";
args += "-- ";
if (!string.IsNullOrEmpty(opts.OrgPath)) args += $"\"{opts.OrgPath}\" ";
args += $"\"{opts.Path}\"";
if (repo.IsLFSFiltered(opts.Path)) {
var lc = Git.Diff.GetLFSChange(repo, args);
if (lc.IsValid) {
SetLFSChange(lc);
} else {
SetSame();
}
return;
}
var rs = Git.Diff.GetTextChange(repo, args);
if (rs.IsBinary) {
SetBinaryChange(Git.Diff.GetSizeChange(repo, opts.RevisionRange, opts.Path, opts.OrgPath));
} else if (rs.Lines.Count > 0) {
lastChanges = rs.Lines;
SetTextChange(); SetTextChange();
} else { } else {
SetSame(); SetSame();
@ -151,7 +202,7 @@ namespace SourceGit.UI {
/// </summary> /// </summary>
/// <param name="rs"></param> /// <param name="rs"></param>
private void SetTextChange() { private void SetTextChange() {
if (lineChanges == null) return; if (lastChanges == null) return;
var fgCommon = FindResource("Brush.FG") as Brush; var fgCommon = FindResource("Brush.FG") as Brush;
var fgIndicator = FindResource("Brush.FG2") as Brush; var fgIndicator = FindResource("Brush.FG2") as Brush;
@ -161,7 +212,7 @@ namespace SourceGit.UI {
if (App.Setting.UI.UseCombinedDiff) { if (App.Setting.UI.UseCombinedDiff) {
var blocks = new List<ChangeBlock>(); var blocks = new List<ChangeBlock>();
foreach (var line in lineChanges) { foreach (var line in lastChanges) {
var block = new ChangeBlock(); var block = new ChangeBlock();
block.Content = line.Content; block.Content = line.Content;
block.Mode = line.Mode; block.Mode = line.Mode;
@ -181,36 +232,52 @@ namespace SourceGit.UI {
loading.Visibility = Visibility.Collapsed; loading.Visibility = Visibility.Collapsed;
textChangeOptions.Visibility = Visibility.Visible; textChangeOptions.Visibility = Visibility.Visible;
var createEditor = editors.Count == 0;
var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine); var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine);
var minWidth = editorContainer.ActualWidth - lineNumberWidth * 2; var minWidth = editorContainer.ActualWidth - lineNumberWidth * 2;
if (editorContainer.ActualHeight < lineChanges.Count * 16) minWidth -= 8; if (editorContainer.ActualHeight < lastChanges.Count * 16) minWidth -= 8;
var editor = CreateTextEditor(new string[] { "OldLine", "NewLine" });
DataGrid editor;
if (createEditor) {
editor = CreateTextEditor(new string[] { "OldLine", "NewLine" });
editor.SetValue(Grid.ColumnSpanProperty, 2);
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) });
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) });
for (int i = 0; i < 2; i++) {
var split = new Rectangle();
split.Width = 1;
split.Fill = FindResource("Brush.Border2") as Brush;
split.HorizontalAlignment = HorizontalAlignment.Right;
Grid.SetColumn(split, i);
editorLines.Children.Add(split);
}
} else {
editor = editors[0] as DataGrid;
editorLines.ColumnDefinitions[0].Width = new GridLength(lineNumberWidth);
editorLines.ColumnDefinitions[0].Width = new GridLength(lineNumberWidth);
}
editor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel); editor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
editor.Columns[1].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel); editor.Columns[1].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
editor.Columns[2].MinWidth = minWidth; editor.Columns[2].MinWidth = minWidth;
editor.ItemsSource = blocks; editor.ItemsSource = blocks;
editor.SetValue(Grid.ColumnSpanProperty, 2);
editorContainer.Children.Add(editor);
editors.Add(editor);
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) }); if (createEditor) {
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) }); editorContainer.Children.Add(editor);
editors.Add(editor);
for (int i = 0; i < 2; i++) { } else {
var split = new Rectangle(); editor.UpdateLayout();
split.Width = 1; editorLines.UpdateLayout();
split.Fill = FindResource("Brush.Border2") as Brush;
split.HorizontalAlignment = HorizontalAlignment.Right;
Grid.SetColumn(split, i);
editorLines.Children.Add(split);
} }
}); });
} else { } else {
var oldSideBlocks = new List<ChangeBlock>(); var oldSideBlocks = new List<ChangeBlock>();
var newSideBlocks = new List<ChangeBlock>(); var newSideBlocks = new List<ChangeBlock>();
foreach (var line in lineChanges) { foreach (var line in lastChanges) {
var block = new ChangeBlock(); var block = new ChangeBlock();
block.Content = line.Content; block.Content = line.Content;
block.Mode = line.Mode; block.Mode = line.Mode;
@ -244,42 +311,63 @@ namespace SourceGit.UI {
loading.Visibility = Visibility.Collapsed; loading.Visibility = Visibility.Collapsed;
textChangeOptions.Visibility = Visibility.Visible; textChangeOptions.Visibility = Visibility.Visible;
var createEditor = editors.Count == 0;
var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine); var lineNumberWidth = CalcLineNumberColWidth(lastOldLine, lastNewLine);
var minWidth = editorContainer.ActualWidth / 2 - lineNumberWidth; var minWidth = editorContainer.ActualWidth / 2 - lineNumberWidth;
if (editorContainer.ActualHeight < newSideBlocks.Count * 16) minWidth -= 8; if (editorContainer.ActualHeight < newSideBlocks.Count * 16) minWidth -= 8;
var oldEditor = CreateTextEditor(new string[] { "OldLine" }); DataGrid oldEditor = null;
oldEditor.SetValue(Grid.ColumnProperty, 0); DataGrid newEditor = null;
oldEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTwoSidesScroll));
if (createEditor) {
oldEditor = CreateTextEditor(new string[] { "OldLine" });
oldEditor.SetValue(Grid.ColumnProperty, 0);
oldEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTwoSidesScroll));
newEditor = CreateTextEditor(new string[] { "NewLine" });
newEditor.SetValue(Grid.ColumnProperty, 1);
newEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTwoSidesScroll));
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) });
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) });
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
for (int i = 0; i < 3; i++) {
var split = new Rectangle();
split.Width = 1;
split.Fill = FindResource("Brush.Border2") as Brush;
split.HorizontalAlignment = HorizontalAlignment.Right;
Grid.SetColumn(split, i);
editorLines.Children.Add(split);
}
} else {
oldEditor = editors[0];
newEditor = editors[1];
editorLines.ColumnDefinitions[0].Width = new GridLength(lineNumberWidth);
editorLines.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
editorLines.ColumnDefinitions[2].Width = new GridLength(lineNumberWidth);
editorLines.ColumnDefinitions[3].Width = new GridLength(1, GridUnitType.Star);
}
oldEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel); oldEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
oldEditor.Columns[1].MinWidth = minWidth; oldEditor.Columns[1].MinWidth = minWidth;
oldEditor.ItemsSource = oldSideBlocks; oldEditor.ItemsSource = oldSideBlocks;
var newEditor = CreateTextEditor(new string[] { "NewLine" });
newEditor.SetValue(Grid.ColumnProperty, 1);
newEditor.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(OnTwoSidesScroll));
newEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel); newEditor.Columns[0].Width = new DataGridLength(lineNumberWidth, DataGridLengthUnitType.Pixel);
newEditor.Columns[1].MinWidth = minWidth; newEditor.Columns[1].MinWidth = minWidth;
newEditor.ItemsSource = newSideBlocks; newEditor.ItemsSource = newSideBlocks;
editorContainer.Children.Add(oldEditor); if (createEditor) {
editorContainer.Children.Add(newEditor); editorContainer.Children.Add(oldEditor);
editorContainer.Children.Add(newEditor);
editors.Add(oldEditor); editors.Add(oldEditor);
editors.Add(newEditor); editors.Add(newEditor);
} else {
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) }); oldEditor.UpdateLayout();
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); newEditor.UpdateLayout();
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(lineNumberWidth) }); editorLines.UpdateLayout();
editorLines.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
for (int i = 0; i < 3; i++) {
var split = new Rectangle();
split.Width = 1;
split.Fill = FindResource("Brush.Border2") as Brush;
split.HorizontalAlignment = HorizontalAlignment.Right;
Grid.SetColumn(split, i);
editorLines.Children.Add(split);
} }
}); });
} }
@ -491,10 +579,28 @@ namespace SourceGit.UI {
return formatted.Width + 16; return formatted.Width + 16;
} }
/// <summary>
/// Clear cached data.
/// </summary>
private void ClearCache() {
lastRepo = null;
lastOpts = null;
lastChanges = null;
}
/// <summary>
/// Clear editor.
/// </summary>
private void ClearEditor() {
editorContainer.Children.Clear();
editorLines.Children.Clear();
editorLines.ColumnDefinitions.Clear();
editors.Clear();
}
#endregion #endregion
#region EVENTS #region EVENTS
/// <summary> /// <summary>
/// Auto fit text change diff size. /// Auto fit text change diff size.
/// </summary> /// </summary>
@ -513,18 +619,23 @@ namespace SourceGit.UI {
editor.Columns[2].Width = DataGridLength.SizeToCells; editor.Columns[2].Width = DataGridLength.SizeToCells;
editor.UpdateLayout(); editor.UpdateLayout();
} else { } else {
var offOld = editors[0].NonFrozenColumnsViewportHorizontalOffset; var oldEditor = editors[0];
var offNew = editors[1].NonFrozenColumnsViewportHorizontalOffset; var newEditor = editors[1];
var offOld = oldEditor.NonFrozenColumnsViewportHorizontalOffset;
var offNew = newEditor.NonFrozenColumnsViewportHorizontalOffset;
var minWidth = total / 2 - Math.Min(offOld, offNew); var minWidth = total / 2 - Math.Min(offOld, offNew);
var scroller = GetVisualChild<ScrollViewer>(editors[0]); var scroller = GetVisualChild<ScrollViewer>(oldEditor);
if (scroller != null && scroller.ComputedVerticalScrollBarVisibility == Visibility.Visible) minWidth -= 8; if (scroller != null && scroller.ComputedVerticalScrollBarVisibility == Visibility.Visible) minWidth -= 8;
editors[0].Columns[1].MinWidth = minWidth;
editors[0].Columns[1].Width = DataGridLength.SizeToCells; oldEditor.Columns[1].MinWidth = minWidth;
editors[1].Columns[1].MinWidth = minWidth; oldEditor.Columns[1].Width = DataGridLength.SizeToCells;
editors[1].Columns[1].Width = DataGridLength.SizeToCells; oldEditor.UpdateLayout();
editors[0].UpdateLayout();
editors[1].UpdateLayout(); newEditor.Columns[1].MinWidth = minWidth;
newEditor.Columns[1].Width = DataGridLength.SizeToCells;
newEditor.UpdateLayout();
} }
} }
@ -543,19 +654,16 @@ namespace SourceGit.UI {
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void OnTwoSidesScroll(object sender, ScrollChangedEventArgs e) { private void OnTwoSidesScroll(object sender, ScrollChangedEventArgs e) {
if (e.VerticalChange != 0) { foreach (var editor in editors) {
foreach (var editor in editors) { var scroller = GetVisualChild<ScrollViewer>(editor);
var scroller = GetVisualChild<ScrollViewer>(editor); if (scroller == null) continue;
if (scroller != null && scroller.VerticalOffset != e.VerticalOffset) {
scroller.ScrollToVerticalOffset(e.VerticalOffset); if (e.VerticalChange != 0 && scroller.VerticalOffset != e.VerticalOffset) {
} scroller.ScrollToVerticalOffset(e.VerticalOffset);
} }
} else {
foreach (var editor in editors) { if (e.HorizontalChange != 0 && scroller.HorizontalOffset != e.HorizontalOffset) {
var scroller = GetVisualChild<ScrollViewer>(editor); scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
if (scroller != null && scroller.HorizontalOffset != e.HorizontalOffset) {
scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
}
} }
} }
} }
@ -624,11 +732,7 @@ namespace SourceGit.UI {
private void ChangeDiffMode(object sender, RoutedEventArgs e) { private void ChangeDiffMode(object sender, RoutedEventArgs e) {
if (!IsLoaded) return; if (!IsLoaded) return;
editorContainer.Children.Clear(); ClearEditor();
editorLines.Children.Clear();
editorLines.ColumnDefinitions.Clear();
editors.Clear();
SetTextChange(); SetTextChange();
} }

View file

@ -96,7 +96,7 @@ namespace SourceGit.UI {
Dispatcher.Invoke(() => { Dispatcher.Invoke(() => {
txtSearch.Text = ""; txtSearch.Text = "";
commitList.ItemsSource = new List<Git.Commit>(cachedCommits); commitList.ItemsSource = cachedCommits;
SetLoadingEnabled(false); SetLoadingEnabled(false);
}); });
} }

View file

@ -73,6 +73,7 @@
<TreeView <TreeView
Grid.Row="1" Grid.Row="1"
x:Name="unstagedTree" x:Name="unstagedTree"
ItemsSource="{Binding ElementName=me, Path=UnstagedTreeData, IsAsync=True}"
Background="{StaticResource Brush.BG2}" Background="{StaticResource Brush.BG2}"
Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.UnstageFileDisplayMode, Converter={StaticResource FilesDisplayModeToTree}}" Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.UnstageFileDisplayMode, Converter={StaticResource FilesDisplayModeToTree}}"
FontFamily="Consolas" FontFamily="Consolas"
@ -127,6 +128,7 @@
<DataGrid <DataGrid
Grid.Row="1" Grid.Row="1"
x:Name="unstagedList" x:Name="unstagedList"
ItemsSource="{Binding ElementName=me, Path=UnstagedListData, IsAsync=True}"
Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.UnstageFileDisplayMode, Converter={StaticResource FilesDisplayModeToList}}" Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.UnstageFileDisplayMode, Converter={StaticResource FilesDisplayModeToList}}"
RowHeight="24" RowHeight="24"
SelectionChanged="UnstagedListSelectionChanged" SelectionChanged="UnstagedListSelectionChanged"
@ -229,6 +231,7 @@
<TreeView <TreeView
Grid.Row="1" Grid.Row="1"
x:Name="stageTree" x:Name="stageTree"
ItemsSource="{Binding ElementName=me, Path=StagedTreeData, IsAsync=True}"
Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.StagedFileDisplayMode, Converter={StaticResource FilesDisplayModeToTree}}" Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.StagedFileDisplayMode, Converter={StaticResource FilesDisplayModeToTree}}"
Background="{StaticResource Brush.BG2}" Background="{StaticResource Brush.BG2}"
FontFamily="Consolas" FontFamily="Consolas"
@ -283,6 +286,7 @@
<DataGrid <DataGrid
Grid.Row="1" Grid.Row="1"
x:Name="stageList" x:Name="stageList"
ItemsSource="{Binding ElementName=me, Path=StagedListData, IsAsync=True}"
RowHeight="24" RowHeight="24"
Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.StagedFileDisplayMode, Converter={StaticResource FilesDisplayModeToList}}" Visibility="{Binding Source={x:Static source:App.Setting}, Path=UI.StagedFileDisplayMode, Converter={StaticResource FilesDisplayModeToList}}"
SelectionChanged="StagedListSelectionChanged" SelectionChanged="StagedListSelectionChanged"

View file

@ -1,8 +1,10 @@
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -26,7 +28,7 @@ namespace SourceGit.UI {
public bool IsFile { get; set; } = false; public bool IsFile { get; set; } = false;
public bool IsNodeExpanded { get; set; } = true; public bool IsNodeExpanded { get; set; } = true;
public Git.Change Change { get; set; } = null; public Git.Change Change { get; set; } = null;
public List<Node> Children { get; set; } = new List<Node>(); public ObservableCollection<Node> Children { get; set; } = new ObservableCollection<Node>();
} }
/// <summary> /// <summary>
@ -40,14 +42,39 @@ namespace SourceGit.UI {
public string CommitMessage { get; set; } public string CommitMessage { get; set; }
/// <summary> /// <summary>
/// Has conflict object? /// Cached unstaged changes in list/grid view.
/// </summary> /// </summary>
private bool hasConflict = false; public ObservableCollection<Git.Change> UnstagedListData { get; set; }
/// <summary>
/// Cached unstaged changes in TreeView.
/// </summary>
public ObservableCollection<Node> UnstagedTreeData { get; set; }
/// <summary>
/// Cached staged changes in list/grid view.
/// </summary>
public ObservableCollection<Git.Change> StagedListData { get; set; }
/// <summary>
/// Cached staged changes in TreeView.
/// </summary>
public ObservableCollection<Node> StagedTreeData { get; set; }
/// <summary>
/// Last view change
/// </summary>
public Git.Change LastViewChange { get; set; }
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
public WorkingCopy() { public WorkingCopy() {
UnstagedListData = new ObservableCollection<Git.Change>();
UnstagedTreeData = new ObservableCollection<Node>();
StagedListData = new ObservableCollection<Git.Change>();
StagedTreeData = new ObservableCollection<Node>();
InitializeComponent(); InitializeComponent();
} }
@ -58,24 +85,35 @@ namespace SourceGit.UI {
public bool SetData(List<Git.Change> changes) { public bool SetData(List<Git.Change> changes) {
List<Git.Change> staged = new List<Git.Change>(); List<Git.Change> staged = new List<Git.Change>();
List<Git.Change> unstaged = new List<Git.Change>(); List<Git.Change> unstaged = new List<Git.Change>();
hasConflict = false; bool hasConflict = false;
bool removeLastViewChange = true;
foreach (var c in changes) { foreach (var c in changes) {
hasConflict = hasConflict || c.IsConflit; hasConflict = hasConflict || c.IsConflit;
if (c.Index != Git.Change.Status.None && c.Index != Git.Change.Status.Untracked) { if (c.IsAddedToIndex) {
staged.Add(c); staged.Add(c);
if (LastViewChange != null && LastViewChange.IsAddedToIndex && c.Path == LastViewChange.Path) {
LastViewChange = c;
removeLastViewChange = false;
}
} }
if (c.WorkTree != Git.Change.Status.None) { if (c.WorkTree != Git.Change.Status.None) {
unstaged.Add(c); unstaged.Add(c);
if (LastViewChange != null && !LastViewChange.IsAddedToIndex && c.Path == LastViewChange.Path) {
LastViewChange = c;
removeLastViewChange = false;
}
} }
} }
SetData(unstaged, true); if (removeLastViewChange) LastViewChange = null;
SetData(staged, false);
Dispatcher.Invoke(() => { Dispatcher.Invoke(() => {
UpdateData(unstaged, UnstagedListData, UnstagedTreeData);
UpdateData(staged, StagedListData, StagedTreeData);
var current = Repo.CurrentBranch(); var current = Repo.CurrentBranch();
if (current != null && !string.IsNullOrEmpty(current.Upstream) && chkAmend.IsChecked != true) { if (current != null && !string.IsNullOrEmpty(current.Upstream) && chkAmend.IsChecked != true) {
btnCommitAndPush.Visibility = Visibility.Visible; btnCommitAndPush.Visibility = Visibility.Visible;
@ -83,13 +121,55 @@ namespace SourceGit.UI {
btnCommitAndPush.Visibility = Visibility.Collapsed; btnCommitAndPush.Visibility = Visibility.Collapsed;
} }
mergePanel.Visibility = Visibility.Collapsed; if (LastViewChange != null) {
diffViewer.Reset(); if (LastViewChange.IsConflit) {
mergePanel.Visibility = Visibility.Visible;
diffViewer.Reset();
} else {
mergePanel.Visibility = Visibility.Collapsed;
diffViewer.Reload();
}
} else {
mergePanel.Visibility = Visibility.Collapsed;
diffViewer.Reset();
}
}); });
return hasConflict; return hasConflict;
} }
/// <summary>
/// Update data.
/// </summary>
/// <param name="changes"></param>
/// <param name="list"></param>
/// <param name="tree"></param>
public void UpdateData(List<Git.Change> changes, ObservableCollection<Git.Change> list, ObservableCollection<Node> tree) {
for (int i = list.Count - 1; i >= 0; i--) {
var exist = list[i];
if (changes.FirstOrDefault(one => one.Path == exist.Path) != null) continue;
list.RemoveAt(i);
RemoveTreeNode(tree, exist);
}
foreach (var c in changes) {
if (list.FirstOrDefault(one => one.Path == c.Path) != null) continue;
bool added = false;
for (int i = 0; i < list.Count; i++) {
if (c.Path.CompareTo(list[i].Path) < 0) {
list.Insert(i, c);
added = true;
break;
}
}
if (!added) list.Add(c);
InsertTreeNode(tree, c);
}
}
/// <summary> /// <summary>
/// Try to load merge message. /// Try to load merge message.
/// </summary> /// </summary>
@ -116,10 +196,10 @@ namespace SourceGit.UI {
/// </summary> /// </summary>
public void Cleanup() { public void Cleanup() {
Repo = null; Repo = null;
unstagedList.ItemsSource = null; UnstagedListData.Clear();
unstagedList.ItemsSource = null; UnstagedTreeData.Clear();
stageList.ItemsSource = null; StagedListData.Clear();
stageTree.ItemsSource = null; StagedTreeData.Clear();
diffViewer.Reset(); diffViewer.Reset();
} }
@ -143,6 +223,8 @@ namespace SourceGit.UI {
return; return;
} }
LastViewChange = node.Change;
DiffViewer.Option opt; DiffViewer.Option opt;
switch (node.Change.WorkTree) { switch (node.Change.WorkTree) {
case Git.Change.Status.Added: case Git.Change.Status.Added:
@ -174,6 +256,8 @@ namespace SourceGit.UI {
return; return;
} }
LastViewChange = change;
DiffViewer.Option opt; DiffViewer.Option opt;
switch (change.WorkTree) { switch (change.WorkTree) {
case Git.Change.Status.Added: case Git.Change.Status.Added:
@ -543,6 +627,8 @@ namespace SourceGit.UI {
var node = selected[0].DataContext as Node; var node = selected[0].DataContext as Node;
if (!node.IsFile) return; if (!node.IsFile) return;
LastViewChange = node.Change;
diffViewer.Diff(Repo, new DiffViewer.Option() { diffViewer.Diff(Repo, new DiffViewer.Option() {
ExtraArgs = "--cached", ExtraArgs = "--cached",
Path = node.FilePath, Path = node.FilePath,
@ -563,6 +649,7 @@ namespace SourceGit.UI {
if (selected.Count != 1) return; if (selected.Count != 1) return;
var change = selected[0] as Git.Change; var change = selected[0] as Git.Change;
LastViewChange = change;
diffViewer.Diff(Repo, new DiffViewer.Option() { diffViewer.Diff(Repo, new DiffViewer.Option() {
ExtraArgs = "--cached", ExtraArgs = "--cached",
Path = change.Path, Path = change.Path,
@ -816,15 +903,16 @@ namespace SourceGit.UI {
} }
private async void Commit(object sender, RoutedEventArgs e) { private async void Commit(object sender, RoutedEventArgs e) {
var amend = chkAmend.IsChecked == true; foreach (var c in UnstagedListData) {
if (c.IsConflit) {
Repo.RecordCommitMessage(CommitMessage); App.RaiseError("You have unsolved conflicts in your working copy!");
return;
if (hasConflict) { }
App.RaiseError("You have unsolved conflicts in your working copy!");
return;
} }
var amend = chkAmend.IsChecked == true;
Repo.RecordCommitMessage(CommitMessage);
if (stageTree.Items.Count == 0) { if (stageTree.Items.Count == 0) {
App.RaiseError("Nothing to commit!"); App.RaiseError("Nothing to commit!");
return; return;
@ -838,15 +926,16 @@ namespace SourceGit.UI {
} }
private async void CommitAndPush(object sender, RoutedEventArgs e) { private async void CommitAndPush(object sender, RoutedEventArgs e) {
var amend = chkAmend.IsChecked == true; foreach (var c in UnstagedListData) {
if (c.IsConflit) {
Repo.RecordCommitMessage(CommitMessage); App.RaiseError("You have unsolved conflicts in your working copy!");
return;
if (hasConflict) { }
App.RaiseError("You have unsolved conflicts in your working copy!");
return;
} }
var amend = chkAmend.IsChecked == true;
Repo.RecordCommitMessage(CommitMessage);
if (stageTree.Items.Count == 0) { if (stageTree.Items.Count == 0) {
App.RaiseError("Nothing to commit!"); App.RaiseError("Nothing to commit!");
return; return;
@ -966,69 +1055,75 @@ namespace SourceGit.UI {
Helpers.TreeViewHelper.SelectWholeTree(tree); Helpers.TreeViewHelper.SelectWholeTree(tree);
} }
private void SetData(List<Git.Change> changes, bool unstaged) { private Node InsertTreeNode(ObservableCollection<Node> nodes, string name, string path, bool isFile = false, Git.Change change = null) {
List<Node> source = new List<Node>(); Node node = new Node();
Dictionary<string, Node> folders = new Dictionary<string, Node>(); node.Name = name;
bool isExpendDefault = changes.Count <= 50; node.FilePath = path;
node.IsFile = isFile;
node.Change = change;
foreach (var c in changes) { bool isAdded = false;
var subs = c.Path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); if (node.IsFile) {
if (subs.Length == 1) { for (var i = 0; i < nodes.Count; i++) {
Node node = new Node(); var cur = nodes[i];
node.FilePath = c.Path; if (!cur.IsFile) continue;
node.IsFile = true; if (node.FilePath.CompareTo(cur.FilePath) > 0) continue;
node.Name = c.Path; nodes.Insert(i, node);
node.Change = c; isAdded = true;
source.Add(node); break;
} else { }
Node lastFolder = null; } else {
var folder = ""; for (var i = 0; i < nodes.Count; i++) {
for (int i = 0; i < subs.Length - 1; i++) { var cur = nodes[i];
folder += (subs[i] + "/"); if (cur.IsFile || node.FilePath.CompareTo(cur.FilePath) < 0) {
if (folders.ContainsKey(folder)) { nodes.Insert(i, node);
lastFolder = folders[folder]; isAdded = true;
} else if (lastFolder == null) { break;
lastFolder = new Node();
lastFolder.FilePath = folder;
lastFolder.Name = subs[i];
lastFolder.IsNodeExpanded = isExpendDefault;
source.Add(lastFolder);
folders.Add(folder, lastFolder);
} else {
var folderNode = new Node();
folderNode.FilePath = folder;
folderNode.Name = subs[i];
folderNode.IsNodeExpanded = isExpendDefault;
folders.Add(folder, folderNode);
lastFolder.Children.Add(folderNode);
lastFolder = folderNode;
}
} }
Node node = new Node();
node.FilePath = c.Path;
node.Name = subs[subs.Length - 1];
node.IsFile = true;
node.Change = c;
lastFolder.Children.Add(node);
} }
} }
folders.Clear(); if (!isAdded) nodes.Add(node);
SortTreeNodes(source); return node;
Dispatcher.Invoke(() => {
if (unstaged) {
unstagedList.ItemsSource = changes;
unstagedTree.ItemsSource = source;
} else {
stageList.ItemsSource = changes;
stageTree.ItemsSource = source;
}
});
} }
private Node FindNodeByPath(List<Node> nodes, string filePath) { private void InsertTreeNode(ObservableCollection<Node> nodes, Git.Change change) {
string[] subs = change.Path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
if (subs.Length == 1) {
InsertTreeNode(nodes, change.Path, change.Path, true, change);
} else {
Node last = nodes.FirstOrDefault(o => o.Name == subs[0]);
if (last == null) last = InsertTreeNode(nodes, subs[0], subs[0]);
for (int i = 1; i < subs.Length - 1; i++) {
var p = last.Children.FirstOrDefault(o => o.Name == subs[i]);
if (p == null) p = InsertTreeNode(last.Children, subs[i], last.FilePath + "/" + subs[i]);
last = p;
}
InsertTreeNode(last.Children, subs[subs.Length - 1], change.Path, true, change);
}
}
private bool RemoveTreeNode(ObservableCollection<Node> nodes, Git.Change change) {
for (int i = nodes.Count - 1; i >= 0; i--) {
if (nodes[i].FilePath == change.Path) {
nodes.RemoveAt(i);
return true;
}
if (nodes[i].IsFile) continue;
if (RemoveTreeNode(nodes[i].Children, change)) {
if (nodes[i].Children.Count == 0) nodes.RemoveAt(i);
return true;
}
}
return false;
}
private Node FindNodeByPath(ObservableCollection<Node> nodes, string filePath) {
foreach (var node in nodes) { foreach (var node in nodes) {
if (node.FilePath == filePath) return node; if (node.FilePath == filePath) return node;
var found = FindNodeByPath(node.Children, filePath); var found = FindNodeByPath(node.Children, filePath);
@ -1037,20 +1132,6 @@ namespace SourceGit.UI {
return null; return null;
} }
private void SortTreeNodes(List<Node> list) {
list.Sort((l, r) => {
if (l.IsFile) {
return r.IsFile ? l.FilePath.CompareTo(r.FilePath) : 1;
} else {
return r.IsFile ? -1 : l.FilePath.CompareTo(r.FilePath);
}
});
foreach (var sub in list) {
if (sub.Children.Count > 0) SortTreeNodes(sub.Children);
}
}
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) { private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView); var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return; if (scroll == null) return;