33from git_sim .animations import handle_animations
44from git_sim .git_sim_base_command import GitSimBaseCommand
55from git_sim .settings import settings
6+ import numpy
7+ import manim as m
68
79
810class Log (GitSimBaseCommand ):
@@ -14,25 +16,164 @@ def __init__(self, commits: int):
1416 self .selected_branches .append (self .repo .active_branch .name )
1517 except TypeError :
1618 pass
19+ self .arrow_map = []
1720
1821 def construct (self ):
1922 if not settings .stdout :
2023 print (f"{ settings .INFO_STRING } { type (self ).__name__ .lower ()} " )
2124 self .show_intro ()
2225 self .get_commits ()
23- self .parse_commits (self .commits [0 ])
26+ self .parse_commits (self .commits [0 ], 0 )
2427 self .recenter_frame ()
2528 self .scale_frame ()
2629 self .fadeout ()
2730 self .show_outro ()
2831
32+ def parse_commits (
33+ self , commit , i , prevCircle = None , shift = numpy .array ([0.0 , 0.0 , 0.0 ]), dots = False
34+ ):
35+ isNewCommit = commit .hexsha not in self .drawnCommits
36+
37+ if i < self .numCommits and commit in self .commits :
38+ commitId , circle , arrow , hide_refs = self .draw_commit (
39+ commit , prevCircle , shift , dots
40+ )
41+
42+ if commit != "dark" :
43+ if not hide_refs and isNewCommit :
44+ self .draw_head (commit , commitId )
45+ self .draw_branch (commit )
46+ self .draw_tag (commit )
47+ if [arrow .start .tolist (), arrow .end .tolist ()] not in self .arrow_map :
48+ self .draw_arrow (prevCircle , arrow )
49+ self .arrow_map .append ([arrow .start .tolist (), arrow .end .tolist ()])
50+ if i == 0 and len (self .drawnRefs ) < 2 :
51+ self .draw_dark_ref ()
52+
53+ if i < len (self .commits ) - 1 :
54+ i += 1
55+ commitParents = list (commit .parents )
56+ if len (commitParents ) > 0 :
57+ # if ( self.args.invert_branches ):
58+ # commitParents.reverse()
59+
60+ # if ( self.args.hide_merged_chains ):
61+ # self.parseCommits(commitParents[0], i+1, prevCircle, toFadeOut)
62+ # else:
63+ for p in range (len (commitParents )):
64+ self .parse_commits (commitParents [p ], i , circle , dots = True )
65+ else :
66+ self .i = 0
67+
68+ def draw_commit (
69+ self , commit , prevCircle , shift = numpy .array ([0.0 , 0.0 , 0.0 ]), dots = False
70+ ):
71+ if commit == "dark" :
72+ commitFill = m .WHITE if settings .light_mode else m .BLACK
73+ elif len (commit .parents ) <= 1 :
74+ commitFill = m .RED
75+ else :
76+ commitFill = m .GRAY
77+
78+ circle = m .Circle (
79+ stroke_color = commitFill , fill_color = commitFill , fill_opacity = 0.25
80+ )
81+ circle .height = 1
82+
83+ if shift .any ():
84+ circle .shift (shift )
85+
86+ if prevCircle :
87+ circle .next_to (
88+ prevCircle , m .RIGHT if settings .reverse else m .LEFT , buff = 1.5
89+ )
90+
91+ while any ((circle .get_center () == c ).all () for c in self .get_centers ()):
92+ circle .next_to (circle , m .DOWN , buff = 3.5 )
93+
94+ isNewCommit = commit .hexsha not in self .drawnCommits
95+
96+ if isNewCommit :
97+ start = (
98+ prevCircle .get_center ()
99+ if prevCircle
100+ else (m .LEFT if settings .reverse else m .RIGHT )
101+ )
102+ end = circle .get_center ()
103+ else :
104+ circle .move_to (self .drawnCommits [commit .hexsha ].get_center ())
105+ start = prevCircle .get_center ()
106+ end = self .drawnCommits [commit .hexsha ].get_center ()
107+
108+ arrow = m .Arrow (start , end , color = self .fontColor )
109+
110+ if commit == "dark" :
111+ arrow = m .Arrow (
112+ start , end , color = m .WHITE if settings .light_mode else m .BLACK
113+ )
114+
115+ length = numpy .linalg .norm (start - end ) - (1.5 if start [1 ] == end [1 ] else 3 )
116+ arrow .set_length (length )
117+ angle = arrow .get_angle ()
118+ lineRect = (
119+ m .Rectangle (height = 0.1 , width = length , color = "#123456" )
120+ .move_to (arrow .get_center ())
121+ .rotate (angle )
122+ )
123+
124+ for commitCircle in self .drawnCommits .values ():
125+ inter = m .Intersection (lineRect , commitCircle )
126+ if inter .has_points ():
127+ arrow = m .CurvedArrow (start , end )
128+ if start [1 ] == end [1 ]:
129+ arrow .shift (UP * 1.25 )
130+ if start [0 ] < end [0 ] and start [1 ] == end [1 ]:
131+ arrow .flip (RIGHT ).shift (UP )
132+
133+ commitId , commitMessage , commit , hide_refs = self .build_commit_id_and_message (
134+ commit , dots
135+ )
136+ commitId .next_to (circle , m .UP )
137+
138+ if commit != "dark" :
139+ self .drawnCommitIds [commit .hexsha ] = commitId
140+
141+ message = m .Text (
142+ "\n " .join (
143+ commitMessage [j : j + 20 ] for j in range (0 , len (commitMessage ), 20 )
144+ )[:100 ],
145+ font = "Monospace" ,
146+ font_size = 14 ,
147+ color = self .fontColor ,
148+ ).next_to (circle , m .DOWN )
149+
150+ if settings .animate and commit != "dark" and isNewCommit :
151+ self .play (
152+ self .camera .frame .animate .move_to (circle .get_center ()),
153+ m .Create (circle ),
154+ m .AddTextLetterByLetter (commitId ),
155+ m .AddTextLetterByLetter (message ),
156+ run_time = 1 / settings .speed ,
157+ )
158+ elif isNewCommit :
159+ self .add (circle , commitId , message )
160+ else :
161+ return commitId , circle , arrow , hide_refs
162+
163+ if commit != "dark" :
164+ self .drawnCommits [commit .hexsha ] = circle
165+
166+ self .toFadeOut .add (circle , commitId , message )
167+ self .prevRef = commitId
168+
169+ return commitId , circle , arrow , hide_refs
170+
29171
30172def log (
31173 commits : int = typer .Option (
32174 default = settings .commits ,
33175 help = "The number of commits to display in the simulated log output" ,
34176 min = 1 ,
35- max = 12 ,
36177 ),
37178):
38179 scene = Log (commits = commits )
0 commit comments