enhance: better indication and context-commands for worktree-linked branch (#1761)

- For worktree-linked branch, `Checkout xyz` will be replaced with `Switch to xyz (worktree)`
- Show worktree path in tooltip of worktree-linked branch
- Add context menu entry `Open` for selected worktree

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-08-26 15:03:35 +08:00
parent db9b13cbce
commit ae737b675b
8 changed files with 47 additions and 19 deletions

View File

@@ -15,7 +15,7 @@ namespace SourceGit.Commands
{
WorkingDirectory = repo;
Context = repo;
Args = "branch -l --all -v --format=\"%(refname)%00%(committerdate:unix)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)\"";
Args = "branch -l --all -v --format=\"%(refname)%00%(committerdate:unix)%00%(objectname)%00%(HEAD)%00%(upstream)%00%(upstream:trackshort)%00%(worktreepath)\"";
}
public async Task<List<Models.Branch>> GetResultAsync()
@@ -61,7 +61,7 @@ namespace SourceGit.Commands
private Models.Branch ParseLine(string line)
{
var parts = line.Split('\0');
if (parts.Length != 6)
if (parts.Length != 7)
return null;
var branch = new Models.Branch();
@@ -109,6 +109,7 @@ namespace SourceGit.Commands
parts[5].Equals("=", StringComparison.Ordinal))
branch.TrackStatus = new Models.BranchTrackStatus();
branch.WorktreePath = parts[6];
return branch;
}
}

View File

@@ -42,6 +42,7 @@ namespace SourceGit.Models
public BranchTrackStatus TrackStatus { get; set; }
public string Remote { get; set; }
public bool IsUpstreamGone { get; set; }
public string WorktreePath { get; set; }
public string FriendlyName => IsLocal ? Name : $"{Remote}/{Name}";
}

View File

@@ -70,6 +70,7 @@
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">Rebase ${0}$ on ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">Rename ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.ResetToSelectedCommit" xml:space="preserve">Reset ${0}$ to ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.SwitchToWorktree" xml:space="preserve">Switch to ${0}$ (worktree)</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">Set Tracking Branch...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">Branch Compare</x:String>
<x:String x:Key="Text.BranchTree.InvalidUpstream" xml:space="preserve">Invalid</x:String>
@@ -874,6 +875,7 @@
<x:String x:Key="Text.Worktree" xml:space="preserve">WORKTREE</x:String>
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">Copy Path</x:String>
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">Lock</x:String>
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">Open</x:String>
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">Remove</x:String>
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">Unlock</x:String>
</ResourceDictionary>

View File

@@ -74,6 +74,7 @@
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">变基(rebase) ${0}$ 至 ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重命名 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.ResetToSelectedCommit" xml:space="preserve">重置 ${0}$ 到 ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.SwitchToWorktree" xml:space="preserve">切换到 ${0}$ (工作树)</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切换上游分支 ...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比较</x:String>
<x:String x:Key="Text.BranchTree.InvalidUpstream" xml:space="preserve">不存在</x:String>
@@ -878,6 +879,7 @@
<x:String x:Key="Text.Worktree" xml:space="preserve">本地工作树</x:String>
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">复制工作树路径</x:String>
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">锁定工作树</x:String>
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">打开工作树</x:String>
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作树</x:String>
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除工作树锁定</x:String>
</ResourceDictionary>

View File

@@ -74,6 +74,7 @@
<x:String x:Key="Text.BranchCM.Rebase" xml:space="preserve">重定基底 (rebase) ${0}$ 分支至 ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.Rename" xml:space="preserve">重新命名 ${0}$...</x:String>
<x:String x:Key="Text.BranchCM.ResetToSelectedCommit" xml:space="preserve">重設 ${0}$ 至 ${1}$...</x:String>
<x:String x:Key="Text.BranchCM.SwitchToWorktree" xml:space="preserve">切換到 ${0}$ (工作區)</x:String>
<x:String x:Key="Text.BranchCM.Tracking" xml:space="preserve">切換上游分支...</x:String>
<x:String x:Key="Text.BranchCompare" xml:space="preserve">分支比較</x:String>
<x:String x:Key="Text.BranchTree.InvalidUpstream" xml:space="preserve">無效</x:String>
@@ -878,6 +879,7 @@
<x:String x:Key="Text.Worktree" xml:space="preserve">本機工作區</x:String>
<x:String x:Key="Text.Worktree.CopyPath" xml:space="preserve">複製工作區路徑</x:String>
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">鎖定工作區</x:String>
<x:String x:Key="Text.Worktree.Open" xml:space="preserve">開啟工作區</x:String>
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作區</x:String>
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除鎖定工作區</x:String>
</ResourceDictionary>

View File

@@ -45,7 +45,17 @@
<Path Width="12" Height="12" Data="{StaticResource Icons.Branch}"/>
<TextBlock FontWeight="Bold" VerticalAlignment="Center" Margin="8,0,0,0" Text="{Binding FriendlyName}"/>
<Border Background="Green" Margin="4,0,0,0" CornerRadius="4" VerticalAlignment="Center" IsVisible="{Binding !IsLocal}">
<TextBlock Text="{DynamicResource Text.BranchTree.Remote}" FontSize="12" Classes="primary" Margin="4,1" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.BranchTree.Remote}" FontSize="12" Classes="primary" Margin="4,0" Foreground="White" VerticalAlignment="Center"/>
</Border>
<Border Background="Gray" Margin="4,0,0,0" CornerRadius="4" VerticalAlignment="Center">
<Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsCurrent" Converter="{x:Static BoolConverters.Not}"/>
<Binding Path="WorktreePath" Converter="{x:Static StringConverters.IsNotNullOrEmpty}"/>
</MultiBinding>
</Border.IsVisible>
<TextBlock Text="{Binding WorktreePath, Mode=OneWay}" FontSize="12" Classes="primary" Margin="4,0" Foreground="White" VerticalAlignment="Center"/>
</Border>
</StackPanel>

View File

@@ -633,22 +633,21 @@ namespace SourceGit.Views
}
else
{
if (!repo.IsBare)
{
var checkout = new MenuItem();
checkout.Header = App.Text("BranchCM.Checkout", branch.Name);
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.Click += async (_, e) =>
{
await repo.CheckoutBranchAsync(branch);
e.Handled = true;
};
menu.Items.Add(checkout);
menu.Items.Add(new MenuItem() { Header = "-" });
}
var hasNoWorktree = string.IsNullOrEmpty(branch.WorktreePath);
var worktree = repo.Worktrees.Find(x => x.Branch == branch.FullName);
if (upstream != null && worktree == null)
var checkout = new MenuItem();
checkout.Header = App.Text(hasNoWorktree ? "BranchCM.Checkout" : "BranchCM.SwitchToWorktree", branch.Name);
checkout.Icon = App.CreateMenuIcon("Icons.Check");
checkout.IsEnabled = !repo.IsBare || !hasNoWorktree;
checkout.Click += async (_, e) =>
{
await repo.CheckoutBranchAsync(branch);
e.Handled = true;
};
menu.Items.Add(checkout);
menu.Items.Add(new MenuItem() { Header = "-" });
if (upstream != null && hasNoWorktree)
{
var fastForward = new MenuItem();
fastForward.Header = App.Text("BranchCM.FastForward", upstream.FriendlyName);
@@ -705,7 +704,7 @@ namespace SourceGit.Views
menu.Items.Add(rebase);
}
if (worktree == null)
if (hasNoWorktree)
{
var selectedCommit = repo.GetSelectedCommitInHistory();
if (selectedCommit != null && !selectedCommit.SHA.Equals(branch.Head, StringComparison.Ordinal))

View File

@@ -190,6 +190,17 @@ namespace SourceGit.Views
{
var menu = new ContextMenu();
var switchTo = new MenuItem();
switchTo.Header = App.Text("Worktree.Open");
switchTo.Icon = App.CreateMenuIcon("Icons.Folder.Open");
switchTo.Click += (_, ev) =>
{
repo.OpenWorktree(worktree);
ev.Handled = true;
};
menu.Items.Add(switchTo);
menu.Items.Add(new MenuItem() { Header = "-" });
if (worktree.IsLocked)
{
var unlock = new MenuItem();