optimize<WorkingCopy>: improve loading time for large number of local changes

This commit is contained in:
leo 2022-11-11 15:01:44 +08:00
parent 4cd07d90a5
commit 5434629f4c
3 changed files with 154 additions and 87 deletions

View file

@ -324,9 +324,10 @@ namespace SourceGit.Views.Widgets {
Task.Run(() => { Task.Run(() => {
var changes = new Commands.LocalChanges(repo.Path, Models.Preference.Instance.Git.IncludeUntrackedInWC).Result(); var changes = new Commands.LocalChanges(repo.Path, Models.Preference.Instance.Git.IncludeUntrackedInWC).Result();
(pages.Get("working_copy") as WorkingCopy).SetData(changes);
Dispatcher.Invoke(() => { Dispatcher.Invoke(() => {
badgeLocalChanges.Label = $"{changes.Count}"; badgeLocalChanges.Label = $"{changes.Count}";
(pages.Get("working_copy") as WorkingCopy).SetData(changes);
UpdateMergeBar(changes); UpdateMergeBar(changes);
}); });
}); });

View file

@ -43,36 +43,38 @@ namespace SourceGit.Views.Widgets {
} }
} }
unstagedContainer.SetData(unstagedChanges); Dispatcher.Invoke(() => {
stagedContainer.SetData(stagedChanges); unstagedContainer.SetData(unstagedChanges);
stagedContainer.SetData(stagedChanges);
var current = repo.Branches.Find(x => x.IsCurrent); var current = repo.Branches.Find(x => x.IsCurrent);
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;
} else { } else {
btnCommitAndPush.Visibility = Visibility.Collapsed; btnCommitAndPush.Visibility = Visibility.Collapsed;
} }
mergePanel.Visibility = Visibility.Collapsed; mergePanel.Visibility = Visibility.Collapsed;
var diffTarget = unstagedContainer.DiffTarget; var diffTarget = unstagedContainer.DiffTarget;
if (diffTarget != null) { if (diffTarget != null) {
if (diffTarget.IsConflit) { if (diffTarget.IsConflit) {
mergePanel.Visibility = Visibility.Visible; mergePanel.Visibility = Visibility.Visible;
diffViewer.Reset();
} else {
diffViewer.Reload();
}
return;
}
diffTarget = stagedContainer.DiffTarget;
if (diffTarget == null) {
diffViewer.Reset(); diffViewer.Reset();
} else { } else {
diffViewer.Reload(); diffViewer.Reload();
} }
});
return;
}
diffTarget = stagedContainer.DiffTarget;
if (diffTarget == null) {
diffViewer.Reset();
} else {
diffViewer.Reload();
}
} }
public void TryLoadMergeMessage() { public void TryLoadMergeMessage() {

View file

@ -173,70 +173,112 @@ namespace SourceGit.Views.Widgets {
public void SetData(List<Models.Change> changes) { public void SetData(List<Models.Change> changes) {
isLoadingData = true; isLoadingData = true;
var oldSet = new Dictionary<string, Models.Change>();
var newSet = new Dictionary<string, Models.Change>();
foreach (var c in changes) newSet.Add(c.Path, c);
for (int i = Changes.Count - 1; i >= 0; i--) {
var old = Changes[i];
if (!newSet.ContainsKey(old.Path)) {
Changes.RemoveAt(i);
RemoveTreeNode(Nodes, old);
if (modeTree.Selected.Contains(old)) modeTree.Selected.Remove(old);
if (DiffTarget == old) DiffTarget = null;
continue;
}
var cur = newSet[old.Path];
if (cur.Index != old.Index || cur.WorkTree != old.WorkTree) {
Changes.RemoveAt(i);
RemoveTreeNode(Nodes, old);
if (modeTree.Selected.Contains(old)) modeTree.Selected.Remove(old);
if (DiffTarget == old) DiffTarget = null;
continue;
}
oldSet.Add(old.Path, old);
}
var isDefaultExpand = changes.Count <= 50; var isDefaultExpand = changes.Count <= 50;
foreach (var c in changes) {
if (oldSet.ContainsKey(c.Path)) continue;
bool added = false; if (changes.Count == 0) {
for (int i = 0; i < Changes.Count; i++) { Changes.Clear();
if (c.Path.CompareTo(Changes[i].Path) < 0) { Nodes.Clear();
Changes.Insert(i, c); } else if (Changes.Count == 0) {
added = true; var nodesMap = new Dictionary<string, ChangeNode>();
break; foreach (var c in changes) {
Changes.Add(c);
int sepIdx = c.Path.IndexOf("/", StringComparison.Ordinal);
if (sepIdx < 0) {
var n = AddTreeNode(Nodes, c.Path, c, false, true);
nodesMap.Add(c.Path, n);
} else {
ObservableCollection<ChangeNode> last = Nodes;
do {
ChangeNode parent = null;
var path = c.Path.Substring(0, sepIdx);
if (!nodesMap.TryGetValue(path, out parent)) {
parent = AddTreeNode(last, path, null, isDefaultExpand, true);
nodesMap.Add(path, parent);
}
last = parent.Children;
sepIdx = c.Path.IndexOf('/', sepIdx + 1);
} while (sepIdx > 0);
var n = AddTreeNode(last, c.Path, c, false, true);
nodesMap.Add(c.Path, n);
} }
} }
nodesMap.Clear();
} else {
var oldSet = new Dictionary<string, Models.Change>();
var newSet = new Dictionary<string, Models.Change>();
foreach (var c in changes) newSet.Add(c.Path, c);
if (!added) Changes.Add(c); for (int i = Changes.Count - 1; i >= 0; i--) {
var old = Changes[i];
if (!newSet.ContainsKey(old.Path)) {
Changes.RemoveAt(i);
RemoveTreeNode(Nodes, old.Path);
if (modeTree.Selected.Contains(old)) modeTree.Selected.Remove(old);
if (DiffTarget == old) DiffTarget = null;
continue;
}
int sepIdx = c.Path.IndexOf("/", StringComparison.Ordinal); var cur = newSet[old.Path];
if (sepIdx < 0) { if (cur.Index != old.Index || cur.WorkTree != old.WorkTree) {
GetOrAddTreeNode(Nodes, c.Path, c, false); Changes.RemoveAt(i);
} else { RemoveTreeNode(Nodes, old.Path);
ObservableCollection<ChangeNode> last = Nodes; if (modeTree.Selected.Contains(old)) modeTree.Selected.Remove(old);
do { if (DiffTarget == old) DiffTarget = null;
var path = c.Path.Substring(0, sepIdx); continue;
last = GetOrAddTreeNode(last, path, null, isDefaultExpand).Children; }
sepIdx = c.Path.IndexOf('/', sepIdx + 1);
} while (sepIdx > 0); oldSet.Add(old.Path, old);
GetOrAddTreeNode(last, c.Path, c, false);
} }
var nodesMap = new Dictionary<string, ChangeNode>();
GetTreeNodes(nodesMap, Nodes);
for (int i = 0; i < changes.Count; i++) {
var c = changes[i];
if (oldSet.ContainsKey(c.Path)) continue;
Changes.Insert(i, c);
int sepIdx = c.Path.IndexOf("/", StringComparison.Ordinal);
if (sepIdx < 0) {
var n = AddTreeNode(Nodes, c.Path, c, false, false);
nodesMap.Add(c.Path, n);
} else {
ObservableCollection<ChangeNode> last = Nodes;
do {
ChangeNode parent = null;
var path = c.Path.Substring(0, sepIdx);
if (!nodesMap.TryGetValue(path, out parent)) {
parent = AddTreeNode(last, path, null, isDefaultExpand, false);
nodesMap.Add(path, parent);
}
last = parent.Children;
sepIdx = c.Path.IndexOf('/', sepIdx + 1);
} while (sepIdx > 0);
var n = AddTreeNode(last, c.Path, c, false, false);
nodesMap.Add(c.Path, n);
}
}
nodesMap.Clear();
} }
isLoadingData = false; isLoadingData = false;
return;
} }
private ChangeNode GetOrAddTreeNode(ObservableCollection<ChangeNode> nodes, string path, Models.Change change, bool isExpand) { private void GetTreeNodes(Dictionary<string, ChangeNode> map, ObservableCollection<ChangeNode> nodes) {
foreach (var n in nodes) { foreach (var n in nodes) {
if (n.Path == path) return n; map.Add(n.Path, n);
if (n.IsFolder) GetTreeNodes(map, n.Children);
} }
}
private ChangeNode AddTreeNode(ObservableCollection<ChangeNode> nodes, string path, Models.Change change, bool isExpand, bool isSorted = false) {
var node = new ChangeNode(); var node = new ChangeNode();
node.Path = path; node.Path = path;
node.Change = change; node.Change = change;
@ -245,19 +287,35 @@ namespace SourceGit.Views.Widgets {
var added = false; var added = false;
if (change == null) { if (change == null) {
for (int i = 0; i < nodes.Count; i++) { for (int i = 0; i < nodes.Count; i++) {
if (!nodes[i].IsFolder || nodes[i].Path.CompareTo(path) > 0) { var n = nodes[i];
if (!n.IsFolder) {
added = true; added = true;
nodes.Add(node); nodes.Insert(i, node);
break;
}
if (isSorted) continue;
if (n.Path.CompareTo(path) > 0) {
added = true;
nodes.Insert(i, node);
break; break;
} }
} }
} else { } else {
for (int i = 0; i < nodes.Count; i++) { if (isSorted) {
if (nodes[i].IsFolder) continue; added = true;
if (nodes[i].Path.CompareTo(path) > 0) { nodes.Add(node);
added = true; } else {
nodes.Add(node); for (int i = 0; i < nodes.Count; i++) {
break; var n = nodes[i];
if (n.IsFolder) continue;
if (n.Path.CompareTo(path) > 0) {
added = true;
nodes.Insert(i, node);
break;
}
} }
} }
} }
@ -266,17 +324,23 @@ namespace SourceGit.Views.Widgets {
return node; return node;
} }
private bool RemoveTreeNode(ObservableCollection<ChangeNode> nodes, Models.Change change) { private bool RemoveTreeNode(ObservableCollection<ChangeNode> nodes, string path) {
for (int i = nodes.Count - 1; i >= 0; i--) { for (int i = nodes.Count - 1; i >= 0; i--) {
var node = nodes[i]; var node = nodes[i];
if (node.Change == null) { if (node.IsFolder) {
if (RemoveTreeNode(node.Children, change)) { if (path.IndexOf(node.Path, StringComparison.Ordinal) < 0) {
continue;
}
if (RemoveTreeNode(node.Children, path)) {
if (node.Children.Count == 0) nodes.RemoveAt(i); if (node.Children.Count == 0) nodes.RemoveAt(i);
return true; return true;
} }
} else if (node.Change.Path == change.Path) { } else {
nodes.RemoveAt(i); if (path.Equals(node.Path, StringComparison.Ordinal)) {
return true; nodes.RemoveAt(i);
return true;
}
} }
} }