Skip to content

Commit b96b0fd

Browse files
committed
feat: add 'Always show current HEAD on the left' option for commit graph
添加“HEAD 路径永远显示在最左边”选项: 1. 在 Preferences 中添加开关及对应多语言翻译。 2. 优化 CommitGraph 解析算法: - 预扫描 HEAD 及其后续提交路径(Forward Lineage)。 - 引入“列预留”机制:在未识别到 HEAD 路径前,强制预留最左侧列。 - 引入“动态重排”机制:在处理每一行前,确保识别到的 HEAD 路径位于活动路径列表的最前端(索引 0)。 - 修复了并行分支因并入 HEAD 祖先而被误判为 HEAD 主干的问题。 3. 确保视觉上 HEAD 路径始终占据最左侧位置,增强复杂分支环境下的可读性。 FAILED test cases: NONE
1 parent 7d4baf0 commit b96b0fd

6 files changed

Lines changed: 83 additions & 14 deletions

File tree

src/Models/CommitGraph.cs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33

44
using Avalonia;
@@ -62,7 +62,7 @@ public class Dot
6262
public List<Link> Links { get; } = [];
6363
public List<Dot> Dots { get; } = [];
6464

65-
public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnabled)
65+
public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnabled, bool alwaysShowCurrentHeadOnLeft)
6666
{
6767
const double unitWidth = 12;
6868
const double halfWidth = 6;
@@ -75,6 +75,24 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
7575
var offsetY = -halfHeight;
7676
var colorPicker = new ColorPicker();
7777

78+
var headPathSHAs = new HashSet<string>();
79+
if (alwaysShowCurrentHeadOnLeft)
80+
{
81+
var head = commits.Find(x => x.IsCurrentHead);
82+
if (head != null)
83+
{
84+
headPathSHAs.Add(head.SHA);
85+
86+
// Forward (newer commits leading to HEAD)
87+
for (int i = commits.IndexOf(head) - 1; i >= 0; i--)
88+
{
89+
if (commits[i].Parents.Count > 0 && headPathSHAs.Contains(commits[i].Parents[0]))
90+
headPathSHAs.Add(commits[i].SHA);
91+
}
92+
}
93+
}
94+
95+
PathHelper headPath = null;
7896
foreach (var commit in commits)
7997
{
8098
PathHelper major = null;
@@ -83,9 +101,26 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
83101
// Update current y offset
84102
offsetY += unitHeight;
85103

86-
// Find first curves that links to this commit and marks others that links to this commit ended.
104+
// 1. Move HEAD path to the front of unsolved list (index 0)
105+
if (alwaysShowCurrentHeadOnLeft && headPath != null && unsolved.Contains(headPath))
106+
{
107+
unsolved.Remove(headPath);
108+
unsolved.Insert(0, headPath);
109+
}
110+
111+
// 2. Reserve leftmost column (offsetX) for HEAD path if not yet found
87112
var offsetX = 4 - halfWidth;
88-
var maxOffsetOld = unsolved.Count > 0 ? unsolved[^1].LastX : offsetX + unitWidth;
113+
if (alwaysShowCurrentHeadOnLeft && headPath == null)
114+
offsetX += unitWidth;
115+
116+
// 3. Keep track of max offsetX used in previous rows for margin calculation
117+
var maxOffsetOld = 0.0;
118+
foreach (var l in unsolved)
119+
maxOffsetOld = Math.Max(maxOffsetOld, l.LastX);
120+
if (unsolved.Count == 0)
121+
maxOffsetOld = offsetX + unitWidth;
122+
123+
// 4. Process existing paths
89124
foreach (var l in unsolved)
90125
{
91126
if (l.Next.Equals(commit.SHA, StringComparison.Ordinal))
@@ -129,14 +164,24 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
129164
}
130165
ended.Clear();
131166

132-
// If no path found, create new curve for branch head
133-
// Otherwise, create new curve for new merged commit
167+
// 5. Create new curve for branch tip or new merged commit
134168
if (major == null)
135169
{
136-
offsetX += unitWidth;
137-
138-
if (commit.Parents.Count > 0)
170+
// If this is the start of the HEAD lineage, place it in the reserved column (index 0)
171+
if (alwaysShowCurrentHeadOnLeft && headPath == null && headPathSHAs.Contains(commit.SHA))
139172
{
173+
if (commit.Parents.Count > 0)
174+
{
175+
var headX = 4 - halfWidth + unitWidth;
176+
major = new PathHelper(commit.Parents[0], isMerged, colorPicker.Next(), new Point(headX, offsetY));
177+
headPath = major;
178+
unsolved.Insert(0, major);
179+
temp.Paths.Add(major.Path);
180+
}
181+
}
182+
else if (commit.Parents.Count > 0)
183+
{
184+
offsetX += unitWidth;
140185
major = new PathHelper(commit.Parents[0], isMerged, colorPicker.Next(), new Point(offsetX, offsetY));
141186
unsolved.Add(major);
142187
temp.Paths.Add(major.Path);

src/Resources/Locales/en_US.axaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@
653653
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">Input path for diff/merge tool</x:String>
654654
<x:String x:Key="Text.Preferences.DiffMerge.Type" xml:space="preserve">Tool</x:String>
655655
<x:String x:Key="Text.Preferences.General" xml:space="preserve">GENERAL</x:String>
656+
<x:String x:Key="Text.Preferences.General.AllowMarkdownNetworkImages" xml:space="preserve">Allow loading network images in Markdown preview</x:String>
657+
<x:String x:Key="Text.Preferences.General.AlwaysShowCurrentHeadOnLeft" xml:space="preserve">Always show current HEAD on the left</x:String>
656658
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">Check for updates on startup</x:String>
657659
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">Date Format</x:String>
658660
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">Enable compact folders in changes tree</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,9 @@
657657
<x:String x:Key="Text.Preferences.DiffMerge.Path.Placeholder" xml:space="preserve">填写工具可执行文件所在位置</x:String>
658658
<x:String x:Key="Text.Preferences.DiffMerge.Type" xml:space="preserve">工具</x:String>
659659
<x:String x:Key="Text.Preferences.General" xml:space="preserve">通用配置</x:String>
660-
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">启动时检测软件更新</x:String>
660+
<x:String x:Key="Text.Preferences.General.AllowMarkdownNetworkImages" xml:space="preserve">允许在 Markdown 预览中加载网络图片</x:String>
661+
<x:String x:Key="Text.Preferences.General.AlwaysShowCurrentHeadOnLeft" xml:space="preserve">HEAD 路径永远显示在最左边</x:String>
662+
<x:String x:Key="Text.Preferences.General.Check4UpdatesOnStartup" xml:space="preserve">启动时检查更新</x:String>
661663
<x:String x:Key="Text.Preferences.General.DateFormat" xml:space="preserve">日期时间格式</x:String>
662664
<x:String x:Key="Text.Preferences.General.EnableCompactFolders" xml:space="preserve">在变更列表树中启用紧凑文件夹模式</x:String>
663665
<x:String x:Key="Text.Preferences.General.Locale" xml:space="preserve">显示语言</x:String>

src/ViewModels/Preferences.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,12 @@ public bool ShowTagsInGraph
217217
set => SetProperty(ref _showTagsInGraph, value);
218218
}
219219

220+
public bool AlwaysShowCurrentHeadOnLeftInGraph
221+
{
222+
get => _alwaysShowCurrentHeadOnLeftInGraph;
223+
set => SetProperty(ref _alwaysShowCurrentHeadOnLeftInGraph, value);
224+
}
225+
220226
public bool UseTwoColumnsLayoutInHistories
221227
{
222228
get => _useTwoColumnsLayoutInHistories;
@@ -833,6 +839,7 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
833839
private string _ignoreUpdateTag = string.Empty;
834840

835841
private bool _showTagsInGraph = true;
842+
private bool _alwaysShowCurrentHeadOnLeftInGraph = false;
836843
private bool _useTwoColumnsLayoutInHistories = false;
837844
private bool _displayTimeAsPeriodInHistories = false;
838845
private bool _useSideBySideDiff = false;

src/ViewModels/Repository.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,8 +1211,10 @@ public void RefreshCommits()
12111211
.Append('-').Append(Preferences.Instance.MaxHistoryCommits).Append(' ')
12121212
.Append(_uiStates.BuildHistoryParams());
12131213

1214-
var commits = await new Commands.QueryCommits(FullPath, builder.ToString()).GetResultAsync().ConfigureAwait(false);
1215-
var graph = Models.CommitGraph.Parse(commits, _uiStates.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly));
1214+
var commits = await new Commands.QueryCommits(FullPath, builder.ToString(), true, patterns).GetResultAsync().ConfigureAwait(false);
1215+
var graph = Models.CommitGraph.Parse(commits,
1216+
_uiStates.HistoryShowFlags.HasFlag(Models.HistoryShowFlags.FirstParentOnly),
1217+
Preferences.Instance.AlwaysShowCurrentHeadOnLeftInGraph);
12161218

12171219
Dispatcher.UIThread.Invoke(() =>
12181220
{

src/Views/Preferences.axaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<TextBlock Classes="tab_header" Text="{DynamicResource Text.Preferences.General}"/>
4949
</TabItem.Header>
5050
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
51+
<Grid Margin="8" RowDefinitions="32,32,32,32,32,32,32,32,32,32,32,32,32,32,Auto" ColumnDefinitions="Auto,*">
5152
<TextBlock Grid.Row="0" Grid.Column="0"
5253
Text="{DynamicResource Text.Preferences.General.Locale}"
5354
HorizontalAlignment="Right"
@@ -167,16 +168,26 @@
167168
IsChecked="{Binding ShowChildren, Mode=TwoWay}"/>
168169

169170
<CheckBox Grid.Row="10" Grid.Column="1"
171+
Height="32"
172+
Content="{DynamicResource Text.Preferences.General.AlwaysShowCurrentHeadOnLeft}"
173+
IsChecked="{Binding AlwaysShowCurrentHeadOnLeftInGraph, Mode=TwoWay}"/>
174+
175+
<CheckBox Grid.Row="11" Grid.Column="1"
170176
Height="32"
171177
Content="{DynamicResource Text.Preferences.General.EnableCompactFolders}"
172178
IsChecked="{Binding EnableCompactFoldersInChangesTree, Mode=TwoWay}"/>
173179

174-
<CheckBox Grid.Row="11" Grid.Column="1"
180+
<CheckBox Grid.Row="12" Grid.Column="1"
175181
Height="32"
176182
Content="{DynamicResource Text.Preferences.General.UseGitHubStyleAvatar}"
177183
IsChecked="{Binding UseGitHubStyleAvatar, Mode=TwoWay}"/>
178184

179-
<CheckBox Grid.Row="12" Grid.Column="1"
185+
<CheckBox Grid.Row="13" Grid.Column="1"
186+
Height="32"
187+
Content="{DynamicResource Text.Preferences.General.AllowMarkdownNetworkImages}"
188+
IsChecked="{Binding AllowMarkdownNetworkImages, Mode=TwoWay}"/>
189+
190+
<CheckBox Grid.Row="14" Grid.Column="1"
180191
Height="32"
181192
Content="{DynamicResource Text.Preferences.General.Check4UpdatesOnStartup}"
182193
IsVisible="{x:Static s:App.IsCheckForUpdateCommandVisible}"

0 commit comments

Comments
 (0)