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,49 +75,114 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
7575 var offsetY = - halfHeight ;
7676 var colorPicker = new ColorPicker ( ) ;
7777
78+ // 1. Pre-scan for the Grand Lineage (Trunk)
79+ var headPathSHAs = new HashSet < string > ( ) ;
80+ string topTrunkSHA = null ;
81+ if ( alwaysShowCurrentHeadOnLeft )
82+ {
83+ var head = commits . Find ( x => x . IsCurrentHead ) ;
84+ if ( head != null )
85+ {
86+ headPathSHAs . Add ( head . SHA ) ;
87+
88+ // Trace DOWN (Ancestors) via first parent
89+ string currentDown = head . SHA ;
90+ for ( int i = commits . IndexOf ( head ) ; i < commits . Count ; i ++ )
91+ {
92+ if ( commits [ i ] . SHA == currentDown )
93+ {
94+ headPathSHAs . Add ( currentDown ) ;
95+ if ( commits [ i ] . Parents . Count > 0 )
96+ currentDown = commits [ i ] . Parents [ 0 ] ;
97+ }
98+ }
99+
100+ // Trace UP (Descendants) via strict single line
101+ string currentUp = head . SHA ;
102+ for ( int i = commits . IndexOf ( head ) - 1 ; i >= 0 ; i -- )
103+ {
104+ if ( commits [ i ] . Parents . Count > 0 && commits [ i ] . Parents [ 0 ] == currentUp )
105+ {
106+ headPathSHAs . Add ( commits [ i ] . SHA ) ;
107+ currentUp = commits [ i ] . SHA ;
108+ }
109+ }
110+
111+ // Find the top-most trunk commit to anchor the Phantom Path
112+ foreach ( var c in commits )
113+ {
114+ if ( headPathSHAs . Contains ( c . SHA ) )
115+ {
116+ topTrunkSHA = c . SHA ;
117+ break ;
118+ }
119+ }
120+ }
121+ }
122+
123+ // 2. Inject Phantom Path for Trunk at Y=0.
124+ // This forces the Trunk to occupy Slot 0 (X=10) permanently, simulating an uncommitted node.
125+ if ( topTrunkSHA != null )
126+ {
127+ var phantom = new PathHelper ( topTrunkSHA , false , colorPicker . Next ( ) , new Point ( 10.0 , offsetY ) ) ;
128+ phantom . IsTrunk = true ;
129+ unsolved . Add ( phantom ) ;
130+ temp . Paths . Add ( phantom . Path ) ;
131+ }
132+
78133 foreach ( var commit in commits )
79134 {
80135 PathHelper major = null ;
81136 var isMerged = commit . IsMerged ;
137+ bool isCommitTrunk = alwaysShowCurrentHeadOnLeft && headPathSHAs . Contains ( commit . SHA ) ;
82138
83139 // Update current y offset
84140 offsetY += unitHeight ;
85141
86142 // 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 ;
143+ var maxOffsetOld = 0.0 ;
144+ for ( int i = 0 ; i < unsolved . Count ; i ++ )
145+ {
146+ if ( unsolved [ i ] . LastX > maxOffsetOld )
147+ maxOffsetOld = unsolved [ i ] . LastX ;
148+ }
149+
150+ var currentOffsetX = 4 - halfWidth ;
89151 foreach ( var l in unsolved )
90152 {
153+ currentOffsetX += unitWidth ;
154+
91155 if ( l . Next . Equals ( commit . SHA , StringComparison . Ordinal ) )
92156 {
93- if ( major == null )
157+ // Only Trunk paths can claim major status for Trunk commits.
158+ bool canBeMajor = ! isCommitTrunk || l . IsTrunk ;
159+
160+ if ( major == null && canBeMajor )
94161 {
95- offsetX += unitWidth ;
96162 major = l ;
97163
98164 if ( commit . Parents . Count > 0 )
99165 {
100166 major . Next = commit . Parents [ 0 ] ;
101- major . Goto ( offsetX , offsetY , halfHeight ) ;
167+ major . Goto ( currentOffsetX , offsetY , halfHeight ) ;
102168 }
103169 else
104170 {
105- major . End ( offsetX , offsetY , halfHeight ) ;
171+ major . End ( currentOffsetX , offsetY , halfHeight ) ;
106172 ended . Add ( l ) ;
107173 }
108174 }
109175 else
110176 {
111- l . End ( major . LastX , offsetY , halfHeight ) ;
177+ l . End ( major ? . LastX ?? currentOffsetX , offsetY , halfHeight ) ;
112178 ended . Add ( l ) ;
113179 }
114180
115181 isMerged = isMerged || l . IsMerged ;
116182 }
117183 else
118184 {
119- offsetX += unitWidth ;
120- l . Pass ( offsetX , offsetY , halfHeight ) ;
185+ l . Pass ( currentOffsetX , offsetY , halfHeight ) ;
121186 }
122187 }
123188
@@ -133,11 +198,10 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
133198 // Otherwise, create new curve for new merged commit
134199 if ( major == null )
135200 {
136- offsetX += unitWidth ;
137-
138201 if ( commit . Parents . Count > 0 )
139202 {
140- major = new PathHelper ( commit . Parents [ 0 ] , isMerged , colorPicker . Next ( ) , new Point ( offsetX , offsetY ) ) ;
203+ currentOffsetX += unitWidth ;
204+ major = new PathHelper ( commit . Parents [ 0 ] , isMerged , colorPicker . Next ( ) , new Point ( currentOffsetX , offsetY ) ) ;
141205 unsolved . Add ( major ) ;
142206 temp . Paths . Add ( major . Path ) ;
143207 }
@@ -149,7 +213,7 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
149213 }
150214
151215 // Calculate link position of this commit.
152- var position = new Point ( major ? . LastX ?? offsetX , offsetY ) ;
216+ var position = new Point ( major ? . LastX ?? Math . Max ( currentOffsetX , 10.0 ) , offsetY ) ;
153217 var dotColor = major ? . Path . Color ?? 0 ;
154218 var anchor = new Dot ( ) { Center = position , Color = dotColor , IsMerged = isMerged } ;
155219 if ( commit . IsCurrentHead )
@@ -187,10 +251,10 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
187251 }
188252 else
189253 {
190- offsetX += unitWidth ;
254+ currentOffsetX += unitWidth ;
191255
192256 // 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 ) ) ;
257+ var l = new PathHelper ( parentHash , isMerged , colorPicker . Next ( ) , position , new Point ( currentOffsetX , position . Y + halfHeight ) ) ;
194258 unsolved . Add ( l ) ;
195259 temp . Paths . Add ( l . Path ) ;
196260 }
@@ -200,7 +264,7 @@ public static CommitGraph Parse(List<Commit> commits, bool firstParentOnlyEnable
200264 // Margins & merge state (used by Views.Histories).
201265 commit . IsMerged = isMerged ;
202266 commit . Color = dotColor ;
203- commit . LeftMargin = Math . Max ( offsetX , maxOffsetOld ) + halfWidth + 2 ;
267+ commit . LeftMargin = Math . Max ( currentOffsetX , maxOffsetOld ) + halfWidth + 2 ;
204268 }
205269
206270 // Deal with curves haven't ended yet.
@@ -246,6 +310,7 @@ private class PathHelper
246310 public Path Path { get ; private set ; }
247311 public string Next { get ; set ; }
248312 public double LastX { get ; private set ; }
313+ public bool IsTrunk { get ; set ; } = false ;
249314
250315 public bool IsMerged => Path . IsMerged ;
251316
@@ -273,9 +338,6 @@ public PathHelper(string next, bool isMerged, int color, Point start, Point to)
273338 /// <summary>
274339 /// A path that just passed this row.
275340 /// </summary>
276- /// <param name="x"></param>
277- /// <param name="y"></param>
278- /// <param name="halfHeight"></param>
279341 public void Pass ( double x , double y , double halfHeight )
280342 {
281343 if ( x > LastX )
@@ -297,9 +359,6 @@ public void Pass(double x, double y, double halfHeight)
297359 /// <summary>
298360 /// A path that has commit in this row but not ended
299361 /// </summary>
300- /// <param name="x"></param>
301- /// <param name="y"></param>
302- /// <param name="halfHeight"></param>
303362 public void Goto ( double x , double y , double halfHeight )
304363 {
305364 if ( x > LastX )
@@ -324,9 +383,6 @@ public void Goto(double x, double y, double halfHeight)
324383 /// <summary>
325384 /// A path that has commit in this row and end.
326385 /// </summary>
327- /// <param name="x"></param>
328- /// <param name="y"></param>
329- /// <param name="halfHeight"></param>
330386 public void End ( double x , double y , double halfHeight )
331387 {
332388 if ( x > LastX )
@@ -385,4 +441,4 @@ private void Add(double x, double y)
385441 Colors . Teal ,
386442 ] ;
387443 }
388- }
444+ }
0 commit comments