Skip to content

Commit aa22679

Browse files
committed
refactor: worktrees in side bar (#2158)
Signed-off-by: leo <longshuang@msn.cn>
1 parent 62a1b3d commit aa22679

File tree

13 files changed

+252
-77
lines changed

13 files changed

+252
-77
lines changed

src/Commands/Worktree.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.IO;
43
using System.Text;
54
using System.Threading.Tasks;
65

@@ -29,7 +28,6 @@ public Worktree(string repo)
2928
if (line.StartsWith("worktree ", StringComparison.Ordinal))
3029
{
3130
last = new Models.Worktree() { FullPath = line.Substring(9).Trim() };
32-
last.RelativePath = Path.GetRelativePath(WorkingDirectory, last.FullPath);
3331
worktrees.Add(last);
3432
continue;
3533
}
@@ -39,8 +37,7 @@ public Worktree(string repo)
3937

4038
if (line.StartsWith("bare", StringComparison.Ordinal))
4139
{
42-
worktrees.Remove(last);
43-
last = null;
40+
last.IsBare = true;
4441
}
4542
else if (line.StartsWith("HEAD ", StringComparison.Ordinal))
4643
{

src/Models/Worktree.cs

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,12 @@
1-
using System;
2-
using CommunityToolkit.Mvvm.ComponentModel;
3-
41
namespace SourceGit.Models
52
{
6-
public class Worktree : ObservableObject
3+
public class Worktree
74
{
85
public string Branch { get; set; } = string.Empty;
96
public string FullPath { get; set; } = string.Empty;
10-
public string RelativePath { get; set; } = string.Empty;
117
public string Head { get; set; } = string.Empty;
8+
public bool IsBare { get; set; } = false;
129
public bool IsDetached { get; set; } = false;
13-
14-
public bool IsLocked
15-
{
16-
get => _isLocked;
17-
set => SetProperty(ref _isLocked, value);
18-
}
19-
20-
public string Name
21-
{
22-
get
23-
{
24-
if (IsDetached)
25-
return $"detached HEAD at {Head.AsSpan(10)}";
26-
27-
if (Branch.StartsWith("refs/heads/", StringComparison.Ordinal))
28-
return Branch.Substring(11);
29-
30-
if (Branch.StartsWith("refs/remotes/", StringComparison.Ordinal))
31-
return Branch.Substring(13);
32-
33-
return Branch;
34-
}
35-
}
36-
37-
private bool _isLocked = false;
10+
public bool IsLocked { get; set; } = false;
3811
}
3912
}

src/Resources/Locales/en_US.axaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,9 +969,12 @@
969969
<x:String x:Key="Text.Workspace" xml:space="preserve">WORKSPACE: </x:String>
970970
<x:String x:Key="Text.Workspace.Configure" xml:space="preserve">Configure Workspaces...</x:String>
971971
<x:String x:Key="Text.Worktree" xml:space="preserve">WORKTREE</x:String>
972+
<x:String x:Key="Text.Worktree.Branch" xml:space="preserve">BRANCH</x:String>
972973
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">Copy Path</x:String>
974+
<x:String x:Key="Text.Worktree.Head" xml:space="preserve">HEAD</x:String>
973975
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">Lock</x:String>
974976
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">Open</x:String>
977+
<x:String x:Key="Text.Worktree.Path" xml:space="preserve">PATH</x:String>
975978
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">Remove</x:String>
976979
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">Unlock</x:String>
977980
<x:String x:Key="Text.Yes" xml:space="preserve">YES</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,9 +973,12 @@
973973
<x:String x:Key="Text.Workspace" xml:space="preserve">工作区:</x:String>
974974
<x:String x:Key="Text.Workspace.Configure" xml:space="preserve">配置工作区...</x:String>
975975
<x:String x:Key="Text.Worktree" xml:space="preserve">本地工作树</x:String>
976+
<x:String x:Key="Text.Worktree.Branch" xml:space="preserve">連結分支</x:String>
976977
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">复制工作树路径</x:String>
978+
<x:String x:Key="Text.Worktree.Head" xml:space="preserve">最新提交</x:String>
977979
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">锁定工作树</x:String>
978980
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">打开工作树</x:String>
981+
<x:String x:Key="Text.Worktree.Path" xml:space="preserve">位置</x:String>
979982
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作树</x:String>
980983
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除工作树锁定</x:String>
981984
<x:String x:Key="Text.Yes" xml:space="preserve">好的</x:String>

src/Resources/Locales/zh_TW.axaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,9 +973,12 @@
973973
<x:String x:Key="Text.Workspace" xml:space="preserve">工作區: </x:String>
974974
<x:String x:Key="Text.Workspace.Configure" xml:space="preserve">設定工作區...</x:String>
975975
<x:String x:Key="Text.Worktree" xml:space="preserve">本機工作區</x:String>
976+
<x:String x:Key="Text.Worktree.Branch" xml:space="preserve">关联分支</x:String>
976977
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">複製工作區路徑</x:String>
978+
<x:String x:Key="Text.Worktree.Head" xml:space="preserve">最新提交</x:String>
977979
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">鎖定工作區</x:String>
978980
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">開啟工作區</x:String>
981+
<x:String x:Key="Text.Worktree.Path" xml:space="preserve">位置</x:String>
979982
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作區</x:String>
980983
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除鎖定工作區</x:String>
981984
<x:String x:Key="Text.Yes" xml:space="preserve">是</x:String>

src/ViewModels/RemoveWorktree.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace SourceGit.ViewModels
44
{
55
public class RemoveWorktree : Popup
66
{
7-
public Models.Worktree Target
7+
public Worktree Target
88
{
99
get;
1010
}
@@ -15,7 +15,7 @@ public bool Force
1515
set;
1616
} = false;
1717

18-
public RemoveWorktree(Repository repo, Models.Worktree target)
18+
public RemoveWorktree(Repository repo, Worktree target)
1919
{
2020
_repo = repo;
2121
Target = target;

src/ViewModels/Repository.cs

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public List<BranchTreeNode> RemoteBranchTrees
172172
private set => SetProperty(ref _remoteBranchTrees, value);
173173
}
174174

175-
public List<Models.Worktree> Worktrees
175+
public List<Worktree> Worktrees
176176
{
177177
get => _worktrees;
178178
private set => SetProperty(ref _worktrees, value);
@@ -1076,23 +1076,7 @@ public void RefreshWorktrees()
10761076
Task.Run(async () =>
10771077
{
10781078
var worktrees = await new Commands.Worktree(FullPath).ReadAllAsync().ConfigureAwait(false);
1079-
if (worktrees.Count == 0)
1080-
{
1081-
Dispatcher.UIThread.Invoke(() => Worktrees = worktrees);
1082-
return;
1083-
}
1084-
1085-
var cleaned = new List<Models.Worktree>();
1086-
foreach (var worktree in worktrees)
1087-
{
1088-
if (worktree.FullPath.Equals(FullPath, StringComparison.Ordinal) ||
1089-
worktree.FullPath.Equals(GitDir, StringComparison.Ordinal))
1090-
continue;
1091-
1092-
cleaned.Add(worktree);
1093-
}
1094-
1095-
Dispatcher.UIThread.Invoke(() => Worktrees = cleaned);
1079+
Dispatcher.UIThread.Invoke(() => Worktrees = Worktree.Build(FullPath, worktrees));
10961080
});
10971081
}
10981082

@@ -1303,7 +1287,7 @@ public async Task CheckoutBranchAsync(Models.Branch branch)
13031287
{
13041288
if (branch.IsLocal)
13051289
{
1306-
var worktree = _worktrees.Find(x => x.Branch.Equals(branch.FullName, StringComparison.Ordinal));
1290+
var worktree = _worktrees.Find(x => x.IsAttachedTo(branch));
13071291
if (worktree != null)
13081292
{
13091293
OpenWorktree(worktree);
@@ -1470,8 +1454,11 @@ public async Task PruneWorktreesAsync()
14701454
await ShowAndStartPopupAsync(new PruneWorktrees(this));
14711455
}
14721456

1473-
public void OpenWorktree(Models.Worktree worktree)
1457+
public void OpenWorktree(Worktree worktree)
14741458
{
1459+
if (worktree.IsCurrent)
1460+
return;
1461+
14751462
var node = Preferences.Instance.FindNode(worktree.FullPath) ??
14761463
new RepositoryNode
14771464
{
@@ -1484,7 +1471,7 @@ public void OpenWorktree(Models.Worktree worktree)
14841471
App.GetLauncher().OpenRepositoryInTab(node, null);
14851472
}
14861473

1487-
public async Task LockWorktreeAsync(Models.Worktree worktree)
1474+
public async Task LockWorktreeAsync(Worktree worktree)
14881475
{
14891476
using var lockWatcher = _watcher?.Lock();
14901477
var log = CreateLog("Lock Worktree");
@@ -1494,7 +1481,7 @@ public async Task LockWorktreeAsync(Models.Worktree worktree)
14941481
log.Complete();
14951482
}
14961483

1497-
public async Task UnlockWorktreeAsync(Models.Worktree worktree)
1484+
public async Task UnlockWorktreeAsync(Worktree worktree)
14981485
{
14991486
using var lockWatcher = _watcher?.Lock();
15001487
var log = CreateLog("Unlock Worktree");
@@ -1864,7 +1851,7 @@ private async Task AutoFetchOnUIThread()
18641851
private Models.Branch _currentBranch = null;
18651852
private List<BranchTreeNode> _localBranchTrees = [];
18661853
private List<BranchTreeNode> _remoteBranchTrees = [];
1867-
private List<Models.Worktree> _worktrees = [];
1854+
private List<Worktree> _worktrees = [];
18681855
private List<Models.Tag> _tags = [];
18691856
private object _visibleTags = null;
18701857
private List<Models.Submodule> _submodules = [];

src/ViewModels/Worktree.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using CommunityToolkit.Mvvm.ComponentModel;
5+
6+
namespace SourceGit.ViewModels
7+
{
8+
public class Worktree : ObservableObject
9+
{
10+
public Models.Worktree Backend { get; private set; }
11+
public bool IsMain { get; private set; }
12+
public bool IsCurrent { get; private set; }
13+
public string DisplayPath { get; private set; }
14+
public string Name { get; private set; }
15+
public string Branch { get; private set; }
16+
public int Depth { get; private set; }
17+
18+
public bool IsLocked
19+
{
20+
get => _isLocked;
21+
set => SetProperty(ref _isLocked, value);
22+
}
23+
24+
public string FullPath => Backend.FullPath;
25+
public string Head => Backend.Head;
26+
27+
public Worktree(DirectoryInfo repo, Models.Worktree wt, bool isMain)
28+
{
29+
Backend = wt;
30+
IsMain = isMain;
31+
IsCurrent = IsCurrentWorktree(repo, wt);
32+
DisplayPath = IsCurrent ? string.Empty : Path.GetRelativePath(repo.FullName, wt.FullPath);
33+
Name = GenerateName();
34+
Branch = GenerateBranchName();
35+
Depth = isMain ? 0 : 1;
36+
IsLocked = wt.IsLocked;
37+
}
38+
39+
public static List<Worktree> Build(string repo, List<Models.Worktree> worktrees)
40+
{
41+
if (worktrees is not { Count: > 1 })
42+
return [];
43+
44+
var repoDir = new DirectoryInfo(repo);
45+
var nodes = new List<Worktree>();
46+
nodes.Add(new(repoDir, worktrees[0], true));
47+
for (int i = 1; i < worktrees.Count; i++)
48+
nodes.Add(new(repoDir, worktrees[i], false));
49+
50+
return nodes;
51+
}
52+
53+
public bool IsAttachedTo(Models.Branch branch)
54+
{
55+
if (string.IsNullOrEmpty(branch.WorktreePath))
56+
return false;
57+
58+
var wtDir = new DirectoryInfo(Backend.FullPath);
59+
var test = new DirectoryInfo(branch.WorktreePath);
60+
return test.FullName.Equals(wtDir.FullName, StringComparison.Ordinal);
61+
}
62+
63+
private bool IsCurrentWorktree(DirectoryInfo repo, Models.Worktree wt)
64+
{
65+
var wtDir = new DirectoryInfo(wt.FullPath);
66+
return wtDir.FullName.Equals(repo.FullName, StringComparison.Ordinal);
67+
}
68+
69+
private string GenerateName()
70+
{
71+
if (IsMain)
72+
return Path.GetFileName(Backend.FullPath);
73+
74+
if (Backend.IsDetached)
75+
return $"detached HEAD at {Backend.Head.AsSpan(10)}";
76+
77+
var b = Backend.Branch;
78+
79+
if (b.StartsWith("refs/heads/", StringComparison.Ordinal))
80+
return b.Substring(11);
81+
82+
if (b.StartsWith("refs/remotes/", StringComparison.Ordinal))
83+
return b.Substring(13);
84+
85+
return b;
86+
}
87+
88+
private string GenerateBranchName()
89+
{
90+
if (Backend.IsBare)
91+
return "-- (default)";
92+
93+
if (Backend.IsDetached)
94+
return "-- (detached)";
95+
96+
if (string.IsNullOrEmpty(Backend.Branch))
97+
return "-- (unknown)";
98+
99+
var b = Backend.Branch;
100+
101+
if (b.StartsWith("refs/heads/", StringComparison.Ordinal))
102+
return b.Substring(11);
103+
104+
if (b.StartsWith("refs/remotes/", StringComparison.Ordinal))
105+
return b.Substring(13);
106+
107+
return b;
108+
}
109+
110+
private bool _isLocked = false;
111+
}
112+
}

src/Views/RemoveWorktree.axaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<Path Grid.Column="0" Width="12" Height="12" Data="{StaticResource Icons.Worktree}"/>
2727
<TextBlock Grid.Column="1" Margin="8,0,0,0" TextTrimming="CharacterEllipsis">
2828
<Run Text="{Binding Target.Name}"/>
29-
<Run Text="{Binding Target.RelativePath}" Foreground="{DynamicResource Brush.FG2}"/>
29+
<Run Text="{Binding Target.FullPath}" Foreground="{DynamicResource Brush.FG2}"/>
3030
</TextBlock>
3131
</Grid>
3232

0 commit comments

Comments
 (0)