Skip to content

Commit c393536

Browse files
Improve rebase subcommand to ignore merge commits by default, flatten diverging branch structure, color rebase dotted arrows, randomly generate simulated commit hashes
Signed-off-by: Jacob Stopak <jacob@initialcommit.io>
1 parent 69044f6 commit c393536

File tree

3 files changed

+94
-35
lines changed

3 files changed

+94
-35
lines changed

src/git_sim/git_sim_base_command.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import os
2-
import platform
3-
import shutil
4-
import stat
2+
import git
53
import sys
4+
import stat
5+
import numpy
6+
import random
7+
import shutil
8+
import platform
69
import tempfile
710

8-
import git
911
import manim as m
10-
import numpy
11-
from git.exc import GitCommandError, InvalidGitRepositoryError
12+
1213
from git.repo import Repo
14+
from git.exc import GitCommandError, InvalidGitRepositoryError
1315

14-
from git_sim.enums import ColorByOptions, StyleOptions
1516
from git_sim.settings import settings
17+
from git_sim.enums import ColorByOptions, StyleOptions
1618

1719

1820
class GitSimBaseCommand(m.MovingCameraScene):
@@ -402,7 +404,7 @@ def get_nonparent_branch_names(self):
402404
exclude = []
403405
for b1 in branches:
404406
for b2 in branches:
405-
if b1.name != b2.name:
407+
if b1.name != b2.name and b1.commit != b2.commit:
406408
if self.repo.is_ancestor(b1.commit, b2.commit):
407409
exclude.append(b1.name)
408410
return [b for b in branches if b.name not in exclude]
@@ -1375,6 +1377,10 @@ def add_ref_to_drawn_refs_by_commit(self, hexsha, ref):
13751377
ref,
13761378
]
13771379

1380+
def generate_random_sha(self):
1381+
valid_chars = "0123456789abcdef"
1382+
return "".join(random.choices(valid_chars, k=6))
1383+
13781384

13791385
class DottedLine(m.Line):
13801386
def __init__(self, *args, dot_spacing=0.4, dot_kwargs={}, **kwargs):

src/git_sim/rebase.py

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import sys
22

33
import git
4-
import manim as m
54
import numpy
5+
import random
6+
7+
import manim as m
68

7-
from git_sim.git_sim_base_command import GitSimBaseCommand
89
from git_sim.settings import settings
10+
from git_sim.git_sim_base_command import GitSimBaseCommand, DottedLine
911

1012

1113
class Rebase(GitSimBaseCommand):
@@ -33,6 +35,16 @@ def __init__(self, branch: str):
3335

3436
self.cmd += f"{type(self).__name__.lower()} {self.branch}"
3537

38+
self.alt_colors = {
39+
0: [m.BLUE_B, m.BLUE_E],
40+
1: [m.PURPLE_B, m.PURPLE_E],
41+
2: [m.RED_B, m.RED_E],
42+
3: [m.GREEN_B, m.GREEN_E],
43+
4: [m.MAROON_B, m.MAROON_E],
44+
5: [m.GOLD_B, m.GOLD_E],
45+
6: [m.TEAL_B, m.TEAL_E],
46+
}
47+
3648
def construct(self):
3749
if not settings.stdout and not settings.output_only_path and not settings.quiet:
3850
print(f"{settings.INFO_STRING} {self.cmd}")
@@ -65,9 +77,12 @@ def construct(self):
6577
branch_commit = self.get_commit(self.branch)
6678
self.parse_commits(branch_commit)
6779
head_commit = self.get_commit()
80+
default_commits = {}
81+
self.get_default_commits(self.get_commit(), default_commits)
82+
default_commits = self.sort_and_flatten(default_commits)
6883

6984
reached_base = False
70-
for commit in self.get_default_commits():
85+
for commit in default_commits:
7186
if commit != "dark" and self.branch in self.repo.git.branch(
7287
"--contains", commit
7388
):
@@ -78,27 +93,45 @@ def construct(self):
7893
self.center_frame_on_commit(branch_commit)
7994

8095
to_rebase = []
81-
i = 0
82-
current = head_commit
83-
while self.branch not in self.repo.git.branch("--contains", current):
84-
to_rebase.append(current)
85-
i += 1
86-
if i >= self.n:
87-
break
88-
current = self.get_default_commits()[i]
96+
for c in default_commits:
97+
if self.branch not in self.repo.git.branch("--contains", c):
98+
to_rebase.append(c)
8999

90100
parent = branch_commit.hexsha
91-
101+
branch_counts = {}
102+
rebased_shas = []
92103
for j, tr in enumerate(reversed(to_rebase)):
104+
if len(tr.parents) > 1:
105+
continue
93106
if not reached_base and j == 0:
94107
message = "..."
95108
else:
96109
message = tr.message
97-
parent = self.setup_and_draw_parent(parent, message)
98-
self.draw_arrow_between_commits(tr.hexsha, parent)
110+
color_index = int(self.drawnCommits[tr.hexsha].get_center()[1] / -4) - 1
111+
if color_index not in branch_counts:
112+
branch_counts[color_index] = 0
113+
branch_counts[color_index] += 1
114+
commit_color = self.alt_colors[color_index % len(self.alt_colors)][1]
115+
parent = self.setup_and_draw_parent(parent, message, color=commit_color)
116+
rebased_shas.append(parent)
99117

100118
self.recenter_frame()
101119
self.scale_frame()
120+
121+
branch_counts = {}
122+
k = 0
123+
for j, tr in enumerate(reversed(to_rebase)):
124+
if len(tr.parents) > 1:
125+
k += 1
126+
continue
127+
color_index = int(self.drawnCommits[tr.hexsha].get_center()[1] / -4) - 1
128+
if color_index not in branch_counts:
129+
branch_counts[color_index] = 0
130+
branch_counts[color_index] += 1
131+
commit_color = self.alt_colors[color_index % len(self.alt_colors)][1]
132+
arrow_color = self.alt_colors[color_index % len(self.alt_colors)][1 if branch_counts[color_index] % 2 == 0 else 1]
133+
self.draw_arrow_between_commits(tr.hexsha, rebased_shas[j - k], color=arrow_color)
134+
102135
self.reset_head_branch(parent)
103136
self.color_by(offset=2 * len(to_rebase))
104137
self.show_command_as_title()
@@ -111,11 +144,12 @@ def setup_and_draw_parent(
111144
commitMessage="New commit",
112145
shift=numpy.array([0.0, 0.0, 0.0]),
113146
draw_arrow=True,
147+
color=m.RED,
114148
):
115149
circle = m.Circle(
116-
stroke_color=m.RED,
150+
stroke_color=color,
117151
stroke_width=self.commit_stroke_width,
118-
fill_color=m.RED,
152+
fill_color=color,
119153
fill_opacity=0.25,
120154
)
121155
circle.height = 1
@@ -139,15 +173,9 @@ def setup_and_draw_parent(
139173
length = numpy.linalg.norm(start - end) - (1.5 if start[1] == end[1] else 3)
140174
arrow.set_length(length)
141175

142-
sha = "".join(
143-
chr(ord(letter) + 1)
144-
if (
145-
(chr(ord(letter) + 1).isalpha() and letter < "f")
146-
or chr(ord(letter) + 1).isdigit()
147-
)
148-
else letter
149-
for letter in child[:6]
150-
)
176+
sha = None
177+
while not sha or sha in self.drawnCommits:
178+
sha = self.generate_random_sha()
151179
commitId = m.Text(
152180
sha if commitMessage != "..." else "...",
153181
font=self.font,
@@ -190,3 +218,28 @@ def setup_and_draw_parent(
190218
self.toFadeOut.add(arrow)
191219

192220
return sha
221+
222+
def get_default_commits(self, commit, default_commits, branch_index=0):
223+
if branch_index not in default_commits:
224+
default_commits[branch_index] = []
225+
if len(default_commits[branch_index]) < self.n:
226+
if commit not in self.sort_and_flatten(default_commits):
227+
default_commits[branch_index].append(commit)
228+
for i, parent in enumerate(commit.parents):
229+
self.get_default_commits(parent, default_commits, branch_index + i)
230+
return default_commits
231+
232+
def draw_arrow_between_commits(self, startsha, endsha, color):
233+
start = self.drawnCommits[startsha].get_center()
234+
end = self.drawnCommits[endsha].get_center()
235+
236+
arrow = DottedLine(
237+
start, end, color=color, dot_kwargs={"color": color}
238+
).add_tip()
239+
length = numpy.linalg.norm(start - end) - 1.65
240+
arrow.set_length(length)
241+
self.draw_arrow(True, arrow)
242+
243+
def sort_and_flatten(self, d):
244+
sorted_values = [d[key] for key in sorted(d.keys(), reverse=True)]
245+
return sum(sorted_values, [])

src/git_sim/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ class Settings(BaseSettings):
2020
transparent_bg: bool = False
2121
logo: pathlib.Path = pathlib.Path(__file__).parent.resolve() / "logo.png"
2222
low_quality: bool = False
23-
max_branches_per_commit: int = 1
24-
max_tags_per_commit: int = 1
23+
max_branches_per_commit: int = 2
24+
max_tags_per_commit: int = 2
2525
media_dir: pathlib.Path = pathlib.Path().cwd()
2626
outro_bottom_text: str = "Learn more at initialcommit.com"
2727
outro_top_text: str = "Thanks for using Initial Commit!"

0 commit comments

Comments
 (0)