1- using System ;
1+ using System ;
22using System . Collections . Generic ;
33
44using 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,23 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
7575 var offsetY = - halfHeight ;
7676 var colorPicker = new ColorPicker ( ) ;
7777
78+ // 1. Pre-scan for HEAD lineage to ensure stability
79+ var headPathSHAs = new HashSet < string > ( ) ;
80+ if ( alwaysShowCurrentHeadOnLeft )
81+ {
82+ var head = commits . Find ( x => x . IsCurrentHead ) ;
83+ if ( head != null )
84+ {
85+ headPathSHAs . Add ( head . SHA ) ;
86+ for ( int i = commits . IndexOf ( head ) - 1 ; i >= 0 ; i -- )
87+ {
88+ if ( commits [ i ] . Parents . Count > 0 && headPathSHAs . Contains ( commits [ i ] . Parents [ 0 ] ) )
89+ headPathSHAs . Add ( commits [ i ] . SHA ) ;
90+ }
91+ }
92+ }
93+
94+ PathHelper headPath = null ;
7895 foreach ( var commit in commits )
7996 {
8097 PathHelper major = null ;
@@ -83,26 +100,44 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
83100 // Update current y offset
84101 offsetY += unitHeight ;
85102
86- // Find first curves that links to this commit and marks others that links to this commit ended.
87- var offsetX = 4 - halfWidth ;
88- var maxOffsetOld = unsolved . Count > 0 ? unsolved [ ^ 1 ] . LastX : offsetX + unitWidth ;
103+ // 2. Reorder unsolved list to keep HEAD lineage at Slot 0
104+ if ( alwaysShowCurrentHeadOnLeft && headPath != null && unsolved . Contains ( headPath ) )
105+ {
106+ unsolved . Remove ( headPath ) ;
107+ unsolved . Insert ( 0 , headPath ) ;
108+ }
109+
110+ // 3. Track max X used in previous rows for margin calculation
111+ var maxOffsetOld = 0.0 ;
112+ for ( int i = 0 ; i < unsolved . Count ; i ++ )
113+ {
114+ if ( unsolved [ i ] . LastX > maxOffsetOld )
115+ maxOffsetOld = unsolved [ i ] . LastX ;
116+ }
117+
118+ // 4. Process existing paths with slot reservation
119+ var currentOffsetX = 4 - halfWidth ;
89120 foreach ( var l in unsolved )
90121 {
122+ currentOffsetX += unitWidth ;
123+
124+ // Reserve Slot 0 (X=10) exclusively for HEAD lineage
125+ if ( alwaysShowCurrentHeadOnLeft && headPathSHAs . Count > 0 && l != headPath && currentOffsetX == 10.0 )
126+ currentOffsetX += unitWidth ;
127+
91128 if ( l . Next . Equals ( commit . SHA , StringComparison . Ordinal ) )
92129 {
93130 if ( major == null )
94131 {
95- offsetX += unitWidth ;
96132 major = l ;
97-
98133 if ( commit . Parents . Count > 0 )
99134 {
100135 major . Next = commit . Parents [ 0 ] ;
101- major . Goto ( offsetX , offsetY , halfHeight ) ;
136+ major . Goto ( currentOffsetX , offsetY , halfHeight ) ;
102137 }
103138 else
104139 {
105- major . End ( offsetX , offsetY , halfHeight ) ;
140+ major . End ( currentOffsetX , offsetY , halfHeight ) ;
106141 ended . Add ( l ) ;
107142 }
108143 }
@@ -116,8 +151,7 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
116151 }
117152 else
118153 {
119- offsetX += unitWidth ;
120- l . Pass ( offsetX , offsetY , halfHeight ) ;
154+ l . Pass ( currentOffsetX , offsetY , halfHeight ) ;
121155 }
122156 }
123157
@@ -129,15 +163,28 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
129163 }
130164 ended . Clear ( ) ;
131165
132- // If no path found, create new curve for branch head
133- // Otherwise, create new curve for new merged commit
166+ // 5. Create new curves for branch tips or merge commits
134167 if ( major == null )
135168 {
136- offsetX += unitWidth ;
137-
138- if ( commit . Parents . Count > 0 )
169+ if ( alwaysShowCurrentHeadOnLeft && headPathSHAs . Contains ( commit . SHA ) )
139170 {
140- major = new PathHelper ( commit . Parents [ 0 ] , isMerged , colorPicker . Next ( ) , new Point ( offsetX , offsetY ) ) ;
171+ if ( commit . Parents . Count > 0 )
172+ {
173+ var headX = 10.0 ;
174+ major = new PathHelper ( commit . Parents [ 0 ] , isMerged , colorPicker . Next ( ) , new Point ( headX , offsetY ) ) ;
175+ headPath = major ;
176+ unsolved . Insert ( 0 , major ) ;
177+ temp . Paths . Add ( major . Path ) ;
178+ if ( headX > currentOffsetX ) currentOffsetX = headX ;
179+ }
180+ }
181+ else if ( commit . Parents . Count > 0 )
182+ {
183+ currentOffsetX += unitWidth ;
184+ if ( alwaysShowCurrentHeadOnLeft && headPathSHAs . Count > 0 && currentOffsetX == 10.0 )
185+ currentOffsetX += unitWidth ;
186+
187+ major = new PathHelper ( commit . Parents [ 0 ] , isMerged , colorPicker . Next ( ) , new Point ( currentOffsetX , offsetY ) ) ;
141188 unsolved . Add ( major ) ;
142189 temp . Paths . Add ( major . Path ) ;
143190 }
@@ -148,8 +195,9 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
148195 temp . Paths . Add ( major . Path ) ;
149196 }
150197
198+
151199 // Calculate link position of this commit.
152- var position = new Point ( major ? . LastX ?? offsetX , offsetY ) ;
200+ var position = new Point ( major ? . LastX ?? Math . Max ( currentOffsetX , 10.0 ) , offsetY ) ;
153201 var dotColor = major ? . Path . Color ?? 0 ;
154202 var anchor = new Dot ( ) { Center = position , Color = dotColor , IsMerged = isMerged } ;
155203 if ( commit . IsCurrentHead )
@@ -187,10 +235,12 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
187235 }
188236 else
189237 {
190- offsetX += unitWidth ;
238+ currentOffsetX += unitWidth ;
239+ if ( alwaysShowCurrentHeadOnLeft && headPathSHAs . Count > 0 && currentOffsetX == 10.0 )
240+ currentOffsetX += unitWidth ;
191241
192242 // Create new curve for parent commit that not includes before
193- var l = new PathHelper ( parentHash , isMerged , colorPicker . Next ( ) , position , new Point ( offsetX , position . Y + halfHeight ) ) ;
243+ var l = new PathHelper ( parentHash , isMerged , colorPicker . Next ( ) , position , new Point ( currentOffsetX , position . Y + halfHeight ) ) ;
194244 unsolved . Add ( l ) ;
195245 temp . Paths . Add ( l . Path ) ;
196246 }
@@ -200,7 +250,7 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
200250 // Margins & merge state (used by Views.Histories).
201251 commit . IsMerged = isMerged ;
202252 commit . Color = dotColor ;
203- commit . LeftMargin = Math . Max ( offsetX , maxOffsetOld ) + halfWidth + 2 ;
253+ commit . LeftMargin = Math . Max ( currentOffsetX , maxOffsetOld ) + halfWidth + 2 ;
204254 }
205255
206256 // Deal with curves haven't ended yet.
0 commit comments