From 08530c34ec311327a297e99874587e0731e5454e Mon Sep 17 00:00:00 2001 From: Jacob Stopak <49353917+initialcommit-io@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:40:39 -0800 Subject: [PATCH 01/19] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6c2ac83 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [initialcommit-com] From 5dfedf1366e4b9aa2a34d5e97488a41017990cb1 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Sun, 26 Feb 2023 21:42:08 -0800 Subject: [PATCH 02/19] Allow diverse branching structures in log subcommand Signed-off-by: Jacob Stopak --- git_sim/log.py | 145 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 2 deletions(-) diff --git a/git_sim/log.py b/git_sim/log.py index f9ecf46..8272c72 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -3,6 +3,8 @@ from git_sim.animations import handle_animations from git_sim.git_sim_base_command import GitSimBaseCommand from git_sim.settings import settings +import numpy +import manim as m class Log(GitSimBaseCommand): @@ -14,25 +16,164 @@ def __init__(self, commits: int): self.selected_branches.append(self.repo.active_branch.name) except TypeError: pass + self.arrow_map = [] def construct(self): if not settings.stdout: print(f"{settings.INFO_STRING} {type(self).__name__.lower()}") self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.fadeout() self.show_outro() + def parse_commits( + self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]), dots=False + ): + isNewCommit = commit.hexsha not in self.drawnCommits + + if i < self.numCommits and commit in self.commits: + commitId, circle, arrow, hide_refs = self.draw_commit( + commit, prevCircle, shift, dots + ) + + if commit != "dark": + if not hide_refs and isNewCommit: + self.draw_head(commit, commitId) + self.draw_branch(commit) + self.draw_tag(commit) + if [arrow.start.tolist(), arrow.end.tolist()] not in self.arrow_map: + self.draw_arrow(prevCircle, arrow) + self.arrow_map.append([arrow.start.tolist(), arrow.end.tolist()]) + if i == 0 and len(self.drawnRefs) < 2: + self.draw_dark_ref() + + if i < len(self.commits) - 1: + i += 1 + commitParents = list(commit.parents) + if len(commitParents) > 0: + # if ( self.args.invert_branches ): + # commitParents.reverse() + + # if ( self.args.hide_merged_chains ): + # self.parseCommits(commitParents[0], i+1, prevCircle, toFadeOut) + # else: + for p in range(len(commitParents)): + self.parse_commits(commitParents[p], i, circle, dots=True) + else: + self.i = 0 + + def draw_commit( + self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0]), dots=False + ): + if commit == "dark": + commitFill = m.WHITE if settings.light_mode else m.BLACK + elif len(commit.parents) <= 1: + commitFill = m.RED + else: + commitFill = m.GRAY + + circle = m.Circle( + stroke_color=commitFill, fill_color=commitFill, fill_opacity=0.25 + ) + circle.height = 1 + + if shift.any(): + circle.shift(shift) + + if prevCircle: + circle.next_to( + prevCircle, m.RIGHT if settings.reverse else m.LEFT, buff=1.5 + ) + + while any((circle.get_center() == c).all() for c in self.get_centers()): + circle.next_to(circle, m.DOWN, buff=3.5) + + isNewCommit = commit.hexsha not in self.drawnCommits + + if isNewCommit: + start = ( + prevCircle.get_center() + if prevCircle + else (m.LEFT if settings.reverse else m.RIGHT) + ) + end = circle.get_center() + else: + circle.move_to(self.drawnCommits[commit.hexsha].get_center()) + start = prevCircle.get_center() + end = self.drawnCommits[commit.hexsha].get_center() + + arrow = m.Arrow(start, end, color=self.fontColor) + + if commit == "dark": + arrow = m.Arrow( + start, end, color=m.WHITE if settings.light_mode else m.BLACK + ) + + length = numpy.linalg.norm(start - end) - (1.5 if start[1] == end[1] else 3) + arrow.set_length(length) + angle = arrow.get_angle() + lineRect = ( + m.Rectangle(height=0.1, width=length, color="#123456") + .move_to(arrow.get_center()) + .rotate(angle) + ) + + for commitCircle in self.drawnCommits.values(): + inter = m.Intersection(lineRect, commitCircle) + if inter.has_points(): + arrow = m.CurvedArrow(start, end) + if start[1] == end[1]: + arrow.shift(UP * 1.25) + if start[0] < end[0] and start[1] == end[1]: + arrow.flip(RIGHT).shift(UP) + + commitId, commitMessage, commit, hide_refs = self.build_commit_id_and_message( + commit, dots + ) + commitId.next_to(circle, m.UP) + + if commit != "dark": + self.drawnCommitIds[commit.hexsha] = commitId + + message = m.Text( + "\n".join( + commitMessage[j : j + 20] for j in range(0, len(commitMessage), 20) + )[:100], + font="Monospace", + font_size=14, + color=self.fontColor, + ).next_to(circle, m.DOWN) + + if settings.animate and commit != "dark" and isNewCommit: + self.play( + self.camera.frame.animate.move_to(circle.get_center()), + m.Create(circle), + m.AddTextLetterByLetter(commitId), + m.AddTextLetterByLetter(message), + run_time=1 / settings.speed, + ) + elif isNewCommit: + self.add(circle, commitId, message) + else: + return commitId, circle, arrow, hide_refs + + if commit != "dark": + self.drawnCommits[commit.hexsha] = circle + + self.toFadeOut.add(circle, commitId, message) + self.prevRef = commitId + + return commitId, circle, arrow, hide_refs + def log( commits: int = typer.Option( default=settings.commits, help="The number of commits to display in the simulated log output", min=1, - max=12, ), ): scene = Log(commits=commits) From 898448893b6ca2ad16e46b5e4393c951c431a5cb Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Sun, 26 Feb 2023 22:26:54 -0800 Subject: [PATCH 03/19] Add --all option to log subcommand Signed-off-by: Jacob Stopak --- git_sim/log.py | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/git_sim/log.py b/git_sim/log.py index 8272c72..943a727 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -8,7 +8,7 @@ class Log(GitSimBaseCommand): - def __init__(self, commits: int): + def __init__(self, commits: int, all: bool): super().__init__() self.numCommits = commits + 1 self.defaultNumCommits = commits + 1 @@ -17,6 +17,7 @@ def __init__(self, commits: int): except TypeError: pass self.arrow_map = [] + self.all = all def construct(self): if not settings.stdout: @@ -24,6 +25,10 @@ def construct(self): self.show_intro() self.get_commits() self.parse_commits(self.commits[0], 0) + if self.all: + for branch in self.repo.branches: + self.get_commits(start=branch.name) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.fadeout() @@ -44,9 +49,21 @@ def parse_commits( self.draw_head(commit, commitId) self.draw_branch(commit) self.draw_tag(commit) - if [arrow.start.tolist(), arrow.end.tolist()] not in self.arrow_map: + if ( + not isinstance(arrow, m.CurvedArrow) + and [arrow.start.tolist(), arrow.end.tolist()] not in self.arrow_map + ): self.draw_arrow(prevCircle, arrow) self.arrow_map.append([arrow.start.tolist(), arrow.end.tolist()]) + elif ( + isinstance(arrow, m.CurvedArrow) + and [arrow.get_start().tolist(), arrow.get_end().tolist()] + not in self.arrow_map + ): + self.draw_arrow(prevCircle, arrow) + self.arrow_map.append( + [arrow.get_start().tolist(), arrow.get_end().tolist()] + ) if i == 0 and len(self.drawnRefs) < 2: self.draw_dark_ref() @@ -102,7 +119,11 @@ def draw_commit( end = circle.get_center() else: circle.move_to(self.drawnCommits[commit.hexsha].get_center()) - start = prevCircle.get_center() + start = ( + prevCircle.get_center() + if prevCircle + else (m.LEFT if settings.reverse else m.RIGHT) + ) end = self.drawnCommits[commit.hexsha].get_center() arrow = m.Arrow(start, end, color=self.fontColor) @@ -126,9 +147,9 @@ def draw_commit( if inter.has_points(): arrow = m.CurvedArrow(start, end) if start[1] == end[1]: - arrow.shift(UP * 1.25) + arrow.shift(m.UP * 1.25) if start[0] < end[0] and start[1] == end[1]: - arrow.flip(RIGHT).shift(UP) + arrow.flip(m.RIGHT).shift(m.UP) commitId, commitMessage, commit, hide_refs = self.build_commit_id_and_message( commit, dots @@ -175,6 +196,10 @@ def log( help="The number of commits to display in the simulated log output", min=1, ), + all: bool = typer.Option( + default=False, + help="Display all local branches in the log output", + ), ): - scene = Log(commits=commits) + scene = Log(commits=commits, all=all) handle_animations(scene=scene) From 4595fbde19161536d5f717cb7bf27545cfd75ab9 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Mon, 27 Feb 2023 21:34:20 -0800 Subject: [PATCH 04/19] Fix color of curved arrows Signed-off-by: Jacob Stopak --- git_sim/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git_sim/log.py b/git_sim/log.py index 943a727..cd6ad59 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -145,7 +145,7 @@ def draw_commit( for commitCircle in self.drawnCommits.values(): inter = m.Intersection(lineRect, commitCircle) if inter.has_points(): - arrow = m.CurvedArrow(start, end) + arrow = m.CurvedArrow(start, end, color=self.fontColor) if start[1] == end[1]: arrow.shift(m.UP * 1.25) if start[0] < end[0] and start[1] == end[1]: From 7a9473de3dcef206da918462b3287a45b27b1edf Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Mon, 27 Feb 2023 23:45:49 -0800 Subject: [PATCH 05/19] Update --all flag to only display non-ancestral branches Signed-off-by: Jacob Stopak --- git_sim/log.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/git_sim/log.py b/git_sim/log.py index cd6ad59..d750583 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -10,8 +10,8 @@ class Log(GitSimBaseCommand): def __init__(self, commits: int, all: bool): super().__init__() - self.numCommits = commits + 1 - self.defaultNumCommits = commits + 1 + self.numCommits = commits + self.defaultNumCommits = commits try: self.selected_branches.append(self.repo.active_branch.name) except TypeError: @@ -26,7 +26,7 @@ def construct(self): self.get_commits() self.parse_commits(self.commits[0], 0) if self.all: - for branch in self.repo.branches: + for branch in self.get_nonparent_branch_names(): self.get_commits(start=branch.name) self.parse_commits(self.commits[0], 0) self.recenter_frame() @@ -35,13 +35,13 @@ def construct(self): self.show_outro() def parse_commits( - self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]), dots=False + self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]) ): isNewCommit = commit.hexsha not in self.drawnCommits if i < self.numCommits and commit in self.commits: commitId, circle, arrow, hide_refs = self.draw_commit( - commit, prevCircle, shift, dots + commit, prevCircle, shift ) if commit != "dark": @@ -67,7 +67,7 @@ def parse_commits( if i == 0 and len(self.drawnRefs) < 2: self.draw_dark_ref() - if i < len(self.commits) - 1: + if i < self.numCommits: # len(self.commits) - 1: i += 1 commitParents = list(commit.parents) if len(commitParents) > 0: @@ -78,13 +78,9 @@ def parse_commits( # self.parseCommits(commitParents[0], i+1, prevCircle, toFadeOut) # else: for p in range(len(commitParents)): - self.parse_commits(commitParents[p], i, circle, dots=True) - else: - self.i = 0 + self.parse_commits(commitParents[p], i, circle) - def draw_commit( - self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0]), dots=False - ): + def draw_commit(self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0])): if commit == "dark": commitFill = m.WHITE if settings.light_mode else m.BLACK elif len(commit.parents) <= 1: @@ -152,7 +148,7 @@ def draw_commit( arrow.flip(m.RIGHT).shift(m.UP) commitId, commitMessage, commit, hide_refs = self.build_commit_id_and_message( - commit, dots + commit ) commitId.next_to(circle, m.UP) @@ -189,6 +185,16 @@ def draw_commit( return commitId, circle, arrow, hide_refs + def get_nonparent_branch_names(self): + branches = [b for b in self.repo.heads if not b.name.startswith("remotes/")] + exclude = [] + for b1 in branches: + for b2 in branches: + if b1.name != b2.name: + if self.repo.is_ancestor(b1.commit, b2.commit): + exclude.append(b1.name) + return [b for b in branches if b.name not in exclude] + def log( commits: int = typer.Option( From 8cd527cf3fce1582a56a737c5c0c03d9f37b3e87 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Mon, 27 Feb 2023 23:52:30 -0800 Subject: [PATCH 06/19] Add --output-only-path global flag to suppress all output except the media file output path Signed-off-by: Jacob Stopak --- README.md | 3 ++- git_sim/__main__.py | 5 +++++ git_sim/add.py | 2 +- git_sim/animations.py | 9 +++++++-- git_sim/branch.py | 2 +- git_sim/cherrypick.py | 2 +- git_sim/commit.py | 2 +- git_sim/log.py | 2 +- git_sim/merge.py | 2 +- git_sim/rebase.py | 2 +- git_sim/reset.py | 2 +- git_sim/restore.py | 2 +- git_sim/revert.py | 2 +- git_sim/settings.py | 1 + git_sim/stash.py | 4 ++-- git_sim/status.py | 2 +- git_sim/tag.py | 2 +- 17 files changed, 29 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1482ecd..1750ee1 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,8 @@ The `[global options]` apply to the overarching `git-sim` simulation itself, inc `-d`: Disable the automatic opening of the image/video file after generation. Useful to avoid errors in console mode with no GUI. `--reverse, -r`: Display commit history in the reverse direction. `--img-format`: Output format for the image file, i.e. `jpg` or `png`. Default output format is `jpg`. -`--stdout`: Write raw image data to stdout while suppressing all other program output. +`--stdout`: Write raw image data to stdout while suppressing all other program output. +`--output-only-path`: Only output the path to the generated media file to stdout. Useful for other programs to ingest. Animation-only global options (to be used in conjunction with `--animate`): diff --git a/git_sim/__main__.py b/git_sim/__main__.py index 17f970d..7e923e1 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -107,6 +107,10 @@ def main( settings.stdout, help="Write raw image data to stdout while suppressing all other program output", ), + output_only_path: bool = typer.Option( + settings.output_only_path, + help="Only output the path to the generated media file to stdout (useful for other programs to ingest)", + ), ): settings.animate = animate settings.auto_open = auto_open @@ -126,6 +130,7 @@ def main( settings.title = title settings.video_format = video_format settings.stdout = stdout + settings.output_only_path = output_only_path if sys.platform == "linux" or sys.platform == "darwin": repo_name = git.repo.Repo( diff --git a/git_sim/add.py b/git_sim/add.py index f6d8fdd..8fce4b6 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -30,7 +30,7 @@ def __init__(self, files: List[str]): sys.exit() def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING} {type(self).__name__.lower()} {' '.join(self.files)}" ) diff --git a/git_sim/animations.py b/git_sim/animations.py index 251ff91..6c98147 100644 --- a/git_sim/animations.py +++ b/git_sim/animations.py @@ -47,12 +47,17 @@ def handle_animations(scene: Scene) -> None: os.path.join(settings.media_dir, "images"), image_file_name ) cv2.imwrite(image_file_path, image) - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print("Output image location:", image_file_path) + elif not settings.stdout and settings.output_only_path: + print(image_file_path) if settings.stdout: sys.stdout.buffer.write(cv2.imencode(".jpg", image)[1].tobytes()) else: - print("Output video location:", scene.renderer.file_writer.movie_file_path) + if not settings.stdout and not settings.output_only_path: + print("Output video location:", scene.renderer.file_writer.movie_file_path) + elif not settings.stdout and settings.output_only_path: + print(scene.renderer.file_writer.movie_file_path) if settings.auto_open and not settings.stdout: try: diff --git a/git_sim/branch.py b/git_sim/branch.py index f47b3c8..8e775c0 100644 --- a/git_sim/branch.py +++ b/git_sim/branch.py @@ -12,7 +12,7 @@ def __init__(self, name: str): self.name = name def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print(f"{settings.INFO_STRING} {type(self).__name__.lower()} {self.name}") self.show_intro() diff --git a/git_sim/cherrypick.py b/git_sim/cherrypick.py index b5280a3..d49e8dd 100644 --- a/git_sim/cherrypick.py +++ b/git_sim/cherrypick.py @@ -34,7 +34,7 @@ def __init__(self, commit: str, edit: str): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING} cherry-pick {self.commit}" + ((' -e "' + self.edit + '"') if self.edit else "") diff --git a/git_sim/commit.py b/git_sim/commit.py index c6caeb1..c345ca6 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -31,7 +31,7 @@ def __init__(self, message: str, amend: bool): sys.exit(1) def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {'--amend ' if self.amend else ''}" + '-m "' diff --git a/git_sim/log.py b/git_sim/log.py index f9ecf46..6acff6e 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -16,7 +16,7 @@ def __init__(self, commits: int): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print(f"{settings.INFO_STRING} {type(self).__name__.lower()}") self.show_intro() self.get_commits() diff --git a/git_sim/merge.py b/git_sim/merge.py index 95fa898..1390dfc 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -37,7 +37,7 @@ def __init__(self, branch: str, no_ff: bool): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.branch} {'--no-ff' if self.no_ff else ''}" ) diff --git a/git_sim/rebase.py b/git_sim/rebase.py index e1d3f26..8f37a84 100644 --- a/git_sim/rebase.py +++ b/git_sim/rebase.py @@ -34,7 +34,7 @@ def __init__(self, branch: str): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.branch}" ) diff --git a/git_sim/reset.py b/git_sim/reset.py index 1522f91..7f6858b 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -49,7 +49,7 @@ def __init__( self.mode = ResetMode.SOFT def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()}{' --' + self.mode.value if self.mode != ResetMode.DEFAULT else ''} {self.commit}", ) diff --git a/git_sim/restore.py b/git_sim/restore.py index b921124..cb5d44b 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -28,7 +28,7 @@ def __init__(self, files: List[str]): sys.exit() def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {' '.join(self.files)}" ) diff --git a/git_sim/revert.py b/git_sim/revert.py index b16af1a..64c57b1 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -36,7 +36,7 @@ def __init__(self, commit: str): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.commit}" ) diff --git a/git_sim/settings.py b/git_sim/settings.py index 563b445..ace8a3b 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -39,6 +39,7 @@ class Settings(BaseSettings): title = "Git-Sim, by initialcommit.com" video_format: VideoFormat = VideoFormat.mp4 stdout = False + output_only_path = False class Config: env_prefix = "git_sim_" diff --git a/git_sim/stash.py b/git_sim/stash.py index 47e75a0..4798a2e 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -44,13 +44,13 @@ def __init__(self, files: List[str], command: StashSubCommand): y.a_path for y in self.repo.index.diff("HEAD") ] elif self.files: - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( "Files are not required in apply/pop subcommand. Ignoring the file list....." ) def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.command.value if self.command else ''} {' '.join(self.files) if not self.no_files else ''}" ) diff --git a/git_sim/status.py b/git_sim/status.py index 0aa4e8f..6942ef2 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -12,7 +12,7 @@ def __init__(self): pass def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print(f"{settings.INFO_STRING } {type(self).__name__.lower()}") self.show_intro() self.get_commits() diff --git a/git_sim/tag.py b/git_sim/tag.py index cbfa058..2080ae2 100644 --- a/git_sim/tag.py +++ b/git_sim/tag.py @@ -12,7 +12,7 @@ def __init__(self, name: str): self.name = name def construct(self): - if not settings.stdout: + if not settings.stdout and not settings.output_only_path: print(f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.name}") self.show_intro() From d8cbb356ce76ad0c74c4b0d90a2b0ca4208a6bd7 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Tue, 28 Feb 2023 00:05:37 -0800 Subject: [PATCH 07/19] Add --quiet, -q flag to suppress all output except errors Signed-off-by: Jacob Stopak --- README.md | 3 ++- git_sim/__main__.py | 7 +++++++ git_sim/add.py | 2 +- git_sim/animations.py | 16 +++++++++++----- git_sim/branch.py | 2 +- git_sim/cherrypick.py | 2 +- git_sim/commit.py | 2 +- git_sim/log.py | 2 +- git_sim/merge.py | 2 +- git_sim/rebase.py | 2 +- git_sim/reset.py | 2 +- git_sim/restore.py | 2 +- git_sim/revert.py | 2 +- git_sim/settings.py | 1 + git_sim/stash.py | 8 ++++++-- git_sim/status.py | 2 +- git_sim/tag.py | 2 +- 17 files changed, 39 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1750ee1..d58c121 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,8 @@ The `[global options]` apply to the overarching `git-sim` simulation itself, inc `--reverse, -r`: Display commit history in the reverse direction. `--img-format`: Output format for the image file, i.e. `jpg` or `png`. Default output format is `jpg`. `--stdout`: Write raw image data to stdout while suppressing all other program output. -`--output-only-path`: Only output the path to the generated media file to stdout. Useful for other programs to ingest. +`--output-only-path`: Only output the path to the generated media file to stdout. Useful for other programs to ingest. +`--quiet, -q`: Suppress all output except errors. Animation-only global options (to be used in conjunction with `--animate`): diff --git a/git_sim/__main__.py b/git_sim/__main__.py index 7e923e1..61de7a6 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -111,6 +111,12 @@ def main( settings.output_only_path, help="Only output the path to the generated media file to stdout (useful for other programs to ingest)", ), + quiet: bool = typer.Option( + settings.quiet, + "--quiet", + "-q", + help="Suppress all output except errors", + ), ): settings.animate = animate settings.auto_open = auto_open @@ -131,6 +137,7 @@ def main( settings.video_format = video_format settings.stdout = stdout settings.output_only_path = output_only_path + settings.quiet = quiet if sys.platform == "linux" or sys.platform == "darwin": repo_name = git.repo.Repo( diff --git a/git_sim/add.py b/git_sim/add.py index 8fce4b6..cbd8cff 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -30,7 +30,7 @@ def __init__(self, files: List[str]): sys.exit() def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING} {type(self).__name__.lower()} {' '.join(self.files)}" ) diff --git a/git_sim/animations.py b/git_sim/animations.py index 6c98147..141ed8d 100644 --- a/git_sim/animations.py +++ b/git_sim/animations.py @@ -47,16 +47,22 @@ def handle_animations(scene: Scene) -> None: os.path.join(settings.media_dir, "images"), image_file_name ) cv2.imwrite(image_file_path, image) - if not settings.stdout and not settings.output_only_path: + if ( + not settings.stdout + and not settings.output_only_path + and not settings.quiet + ): print("Output image location:", image_file_path) - elif not settings.stdout and settings.output_only_path: + elif ( + not settings.stdout and settings.output_only_path and not settings.quiet + ): print(image_file_path) - if settings.stdout: + if settings.stdout and not settings.quiet: sys.stdout.buffer.write(cv2.imencode(".jpg", image)[1].tobytes()) else: - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print("Output video location:", scene.renderer.file_writer.movie_file_path) - elif not settings.stdout and settings.output_only_path: + elif not settings.stdout and settings.output_only_path and not settings.quiet: print(scene.renderer.file_writer.movie_file_path) if settings.auto_open and not settings.stdout: diff --git a/git_sim/branch.py b/git_sim/branch.py index 8e775c0..55c140e 100644 --- a/git_sim/branch.py +++ b/git_sim/branch.py @@ -12,7 +12,7 @@ def __init__(self, name: str): self.name = name def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print(f"{settings.INFO_STRING} {type(self).__name__.lower()} {self.name}") self.show_intro() diff --git a/git_sim/cherrypick.py b/git_sim/cherrypick.py index d49e8dd..0b4a903 100644 --- a/git_sim/cherrypick.py +++ b/git_sim/cherrypick.py @@ -34,7 +34,7 @@ def __init__(self, commit: str, edit: str): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING} cherry-pick {self.commit}" + ((' -e "' + self.edit + '"') if self.edit else "") diff --git a/git_sim/commit.py b/git_sim/commit.py index c345ca6..c13526b 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -31,7 +31,7 @@ def __init__(self, message: str, amend: bool): sys.exit(1) def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {'--amend ' if self.amend else ''}" + '-m "' diff --git a/git_sim/log.py b/git_sim/log.py index 6acff6e..ba24bd1 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -16,7 +16,7 @@ def __init__(self, commits: int): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print(f"{settings.INFO_STRING} {type(self).__name__.lower()}") self.show_intro() self.get_commits() diff --git a/git_sim/merge.py b/git_sim/merge.py index 1390dfc..3d1bc09 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -37,7 +37,7 @@ def __init__(self, branch: str, no_ff: bool): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.branch} {'--no-ff' if self.no_ff else ''}" ) diff --git a/git_sim/rebase.py b/git_sim/rebase.py index 8f37a84..d0a5a95 100644 --- a/git_sim/rebase.py +++ b/git_sim/rebase.py @@ -34,7 +34,7 @@ def __init__(self, branch: str): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.branch}" ) diff --git a/git_sim/reset.py b/git_sim/reset.py index 7f6858b..266d1af 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -49,7 +49,7 @@ def __init__( self.mode = ResetMode.SOFT def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()}{' --' + self.mode.value if self.mode != ResetMode.DEFAULT else ''} {self.commit}", ) diff --git a/git_sim/restore.py b/git_sim/restore.py index cb5d44b..0f24538 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -28,7 +28,7 @@ def __init__(self, files: List[str]): sys.exit() def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {' '.join(self.files)}" ) diff --git a/git_sim/revert.py b/git_sim/revert.py index 64c57b1..892d6d4 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -36,7 +36,7 @@ def __init__(self, commit: str): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.commit}" ) diff --git a/git_sim/settings.py b/git_sim/settings.py index ace8a3b..54bd63f 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -40,6 +40,7 @@ class Settings(BaseSettings): video_format: VideoFormat = VideoFormat.mp4 stdout = False output_only_path = False + quiet = False class Config: env_prefix = "git_sim_" diff --git a/git_sim/stash.py b/git_sim/stash.py index 4798a2e..aeb66c0 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -44,13 +44,17 @@ def __init__(self, files: List[str], command: StashSubCommand): y.a_path for y in self.repo.index.diff("HEAD") ] elif self.files: - if not settings.stdout and not settings.output_only_path: + if ( + not settings.stdout + and not settings.output_only_path + and not settings.quiet + ): print( "Files are not required in apply/pop subcommand. Ignoring the file list....." ) def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.command.value if self.command else ''} {' '.join(self.files) if not self.no_files else ''}" ) diff --git a/git_sim/status.py b/git_sim/status.py index 6942ef2..f1fea42 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -12,7 +12,7 @@ def __init__(self): pass def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print(f"{settings.INFO_STRING } {type(self).__name__.lower()}") self.show_intro() self.get_commits() diff --git a/git_sim/tag.py b/git_sim/tag.py index 2080ae2..71b3074 100644 --- a/git_sim/tag.py +++ b/git_sim/tag.py @@ -12,7 +12,7 @@ def __init__(self, name: str): self.name = name def construct(self): - if not settings.stdout and not settings.output_only_path: + if not settings.stdout and not settings.output_only_path and not settings.quiet: print(f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.name}") self.show_intro() From 7564b67e9da74da21e54b430e916fcda0e756a98 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Tue, 28 Feb 2023 11:06:35 -0800 Subject: [PATCH 08/19] Add --all flag to log subcommand output when used Signed-off-by: Jacob Stopak --- git_sim/log.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git_sim/log.py b/git_sim/log.py index d750583..6d653ca 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -21,7 +21,9 @@ def __init__(self, commits: int, all: bool): def construct(self): if not settings.stdout: - print(f"{settings.INFO_STRING} {type(self).__name__.lower()}") + print( + f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all else ''}" + ) self.show_intro() self.get_commits() self.parse_commits(self.commits[0], 0) From e3afcc89d438d5e2653458243972f8fb40cf545d Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Tue, 28 Feb 2023 11:30:26 -0800 Subject: [PATCH 09/19] Add global flags for --invert-branches and --hide-merged-chains Signed-off-by: Jacob Stopak --- README.md | 4 +++- git_sim/__main__.py | 10 ++++++++++ git_sim/log.py | 16 ++++++++-------- git_sim/settings.py | 2 ++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1482ecd..59650b3 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,9 @@ The `[global options]` apply to the overarching `git-sim` simulation itself, inc `-d`: Disable the automatic opening of the image/video file after generation. Useful to avoid errors in console mode with no GUI. `--reverse, -r`: Display commit history in the reverse direction. `--img-format`: Output format for the image file, i.e. `jpg` or `png`. Default output format is `jpg`. -`--stdout`: Write raw image data to stdout while suppressing all other program output. +`--stdout`: Write raw image data to stdout while suppressing all other program output. +`--invert-branches`: Invert positioning of branches by reversing order of multiple parents where applicable. +`--hide-merged-chains`: Hide commits from merged branches, i.e. only display mainline commits. Animation-only global options (to be used in conjunction with `--animate`): diff --git a/git_sim/__main__.py b/git_sim/__main__.py index 17f970d..e842acf 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -107,6 +107,14 @@ def main( settings.stdout, help="Write raw image data to stdout while suppressing all other program output", ), + invert_branches: bool = typer.Option( + settings.invert_branches, + help="Invert positioning of branches by reversing order of multiple parents where applicable", + ), + hide_merged_chains: bool = typer.Option( + settings.hide_merged_chains, + help="Hide commits from merged branches, i.e. only display mainline commits", + ), ): settings.animate = animate settings.auto_open = auto_open @@ -126,6 +134,8 @@ def main( settings.title = title settings.video_format = video_format settings.stdout = stdout + settings.invert_branches = invert_branches + settings.hide_merged_chains = hide_merged_chains if sys.platform == "linux" or sys.platform == "darwin": repo_name = git.repo.Repo( diff --git a/git_sim/log.py b/git_sim/log.py index 6d653ca..0df25a8 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -73,14 +73,14 @@ def parse_commits( i += 1 commitParents = list(commit.parents) if len(commitParents) > 0: - # if ( self.args.invert_branches ): - # commitParents.reverse() - - # if ( self.args.hide_merged_chains ): - # self.parseCommits(commitParents[0], i+1, prevCircle, toFadeOut) - # else: - for p in range(len(commitParents)): - self.parse_commits(commitParents[p], i, circle) + if settings.invert_branches: + commitParents.reverse() + + if settings.hide_merged_chains: + self.parse_commits(commitParents[0], i, circle) + else: + for p in range(len(commitParents)): + self.parse_commits(commitParents[p], i, circle) def draw_commit(self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0])): if commit == "dark": diff --git a/git_sim/settings.py b/git_sim/settings.py index 563b445..2ecf3aa 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -39,6 +39,8 @@ class Settings(BaseSettings): title = "Git-Sim, by initialcommit.com" video_format: VideoFormat = VideoFormat.mp4 stdout = False + invert_branches = False + hide_merged_chains = False class Config: env_prefix = "git_sim_" From 3533002e3e726fa991038aca347c51d5f208b8c6 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Tue, 28 Feb 2023 16:38:57 -0800 Subject: [PATCH 10/19] Refactor all subcommands to accomodate new diverse branching system Signed-off-by: Jacob Stopak --- git_sim/add.py | 3 +- git_sim/branch.py | 2 +- git_sim/cherrypick.py | 4 +- git_sim/commit.py | 3 +- git_sim/git_sim_base_command.py | 146 +++++++++++++++++++--------- git_sim/log.py | 162 -------------------------------- git_sim/merge.py | 7 +- git_sim/rebase.py | 7 +- git_sim/reset.py | 9 +- git_sim/restore.py | 3 +- git_sim/revert.py | 8 +- git_sim/stash.py | 3 +- git_sim/status.py | 3 +- git_sim/tag.py | 2 +- 14 files changed, 127 insertions(+), 235 deletions(-) diff --git a/git_sim/add.py b/git_sim/add.py index f6d8fdd..75e5a43 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -16,6 +16,7 @@ def __init__(self, files: List[str]): self.hide_first_tag = True self.allow_no_commits = True self.files = files + settings.hide_merged_chains = True try: self.selected_branches.append(self.repo.active_branch.name) @@ -37,7 +38,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/branch.py b/git_sim/branch.py index f47b3c8..068c015 100644 --- a/git_sim/branch.py +++ b/git_sim/branch.py @@ -17,7 +17,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() diff --git a/git_sim/cherrypick.py b/git_sim/cherrypick.py index b5280a3..6667868 100644 --- a/git_sim/cherrypick.py +++ b/git_sim/cherrypick.py @@ -54,10 +54,10 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.orig_commits = self.commits self.get_commits(start=self.commit) - self.parse_commits(self.commits[0], shift=4 * m.DOWN) + self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) self.center_frame_on_commit(self.orig_commits[0]) self.setup_and_draw_parent( self.orig_commits[0], diff --git a/git_sim/commit.py b/git_sim/commit.py index c6caeb1..ac5c1df 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -18,6 +18,7 @@ def __init__(self, message: str, amend: bool): self.defaultNumCommits = 4 if not self.amend else 5 self.numCommits = 4 if not self.amend else 5 self.hide_first_tag = True + settings.hide_merged_chains = True try: self.selected_branches.append(self.repo.active_branch.name) @@ -51,7 +52,7 @@ def construct(self): ) self.commits[0] = amended - self.parse_commits(self.commits[self.i]) + self.parse_commits(self.commits[0], 0) self.center_frame_on_commit(self.commits[0]) if not self.amend: diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index 8e65e80..f7587d9 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -25,12 +25,12 @@ def __init__(self): self.trimmed = False self.prevRef = None self.topref = None - self.i = 0 self.numCommits = settings.commits self.defaultNumCommits = settings.commits self.selected_branches = [] self.stop = False self.zone_title_offset = 2.6 if platform.system() == "Windows" else 2.6 + self.arrow_map = [] self.logo = m.ImageMobject(settings.logo) self.logo.width = 3 @@ -81,31 +81,50 @@ def get_commits(self, start="HEAD"): self.get_commits(start=start) def parse_commits( - self, commit, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]), dots=False + self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]) ): - if self.stop: - return - if self.i < self.numCommits and commit in self.commits: + isNewCommit = commit.hexsha not in self.drawnCommits + + if i < self.numCommits and commit in self.commits: commitId, circle, arrow, hide_refs = self.draw_commit( - commit, prevCircle, shift, dots + commit, i, prevCircle, shift ) if commit != "dark": - if not hide_refs and not self.stop: - self.draw_head(commit, commitId) - self.draw_branch(commit) - self.draw_tag(commit) - self.draw_arrow(prevCircle, arrow) - if self.stop: - return - if self.i == 0 and len(self.drawnRefs) < 2: + if not hide_refs and isNewCommit: + self.draw_head(commit, i, commitId) + self.draw_branch(commit, i) + self.draw_tag(commit, i) + if ( + not isinstance(arrow, m.CurvedArrow) + and [arrow.start.tolist(), arrow.end.tolist()] not in self.arrow_map + ): + self.draw_arrow(prevCircle, arrow) + self.arrow_map.append([arrow.start.tolist(), arrow.end.tolist()]) + elif ( + isinstance(arrow, m.CurvedArrow) + and [arrow.get_start().tolist(), arrow.get_end().tolist()] + not in self.arrow_map + ): + self.draw_arrow(prevCircle, arrow) + self.arrow_map.append( + [arrow.get_start().tolist(), arrow.get_end().tolist()] + ) + if i == 0 and len(self.drawnRefs) < 2: self.draw_dark_ref() - if self.i < len(self.commits) - 1: - self.i += 1 - self.parse_commits(self.commits[self.i], circle, dots=True) - else: - self.i = 0 + if i < self.numCommits: # len(self.commits) - 1: + i += 1 + commitParents = list(commit.parents) + if len(commitParents) > 0: + if settings.invert_branches: + commitParents.reverse() + + if settings.hide_merged_chains: + self.parse_commits(commitParents[0], i, circle) + else: + for p in range(len(commitParents)): + self.parse_commits(commitParents[p], i, circle) def show_intro(self): if settings.animate and settings.show_intro: @@ -170,9 +189,7 @@ def get_centers(self): centers.append(commit.get_center()) return centers - def draw_commit( - self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0]), dots=False - ): + def draw_commit(self, commit, i, prevCircle, shift=numpy.array([0.0, 0.0, 0.0])): if commit == "dark": commitFill = m.WHITE if settings.light_mode else m.BLACK elif len(commit.parents) <= 1: @@ -193,29 +210,54 @@ def draw_commit( prevCircle, m.RIGHT if settings.reverse else m.LEFT, buff=1.5 ) - start = ( - prevCircle.get_center() - if prevCircle - else (m.LEFT if settings.reverse else m.RIGHT) - ) - end = circle.get_center() + while any((circle.get_center() == c).all() for c in self.get_centers()): + circle.next_to(circle, m.DOWN, buff=3.5) + + isNewCommit = commit.hexsha not in self.drawnCommits + + if isNewCommit: + start = ( + prevCircle.get_center() + if prevCircle + else (m.LEFT if settings.reverse else m.RIGHT) + ) + end = circle.get_center() + else: + circle.move_to(self.drawnCommits[commit.hexsha].get_center()) + start = ( + prevCircle.get_center() + if prevCircle + else (m.LEFT if settings.reverse else m.RIGHT) + ) + end = self.drawnCommits[commit.hexsha].get_center() + + arrow = m.Arrow(start, end, color=self.fontColor) if commit == "dark": arrow = m.Arrow( start, end, color=m.WHITE if settings.light_mode else m.BLACK ) - elif commit.hexsha in self.drawnCommits: - end = self.drawnCommits[commit.hexsha].get_center() - arrow = m.Arrow(start, end, color=self.fontColor) - self.stop = True - else: - arrow = m.Arrow(start, end, color=self.fontColor) length = numpy.linalg.norm(start - end) - (1.5 if start[1] == end[1] else 3) arrow.set_length(length) + angle = arrow.get_angle() + lineRect = ( + m.Rectangle(height=0.1, width=length, color="#123456") + .move_to(arrow.get_center()) + .rotate(angle) + ) + + for commitCircle in self.drawnCommits.values(): + inter = m.Intersection(lineRect, commitCircle) + if inter.has_points(): + arrow = m.CurvedArrow(start, end, color=self.fontColor) + if start[1] == end[1]: + arrow.shift(m.UP * 1.25) + if start[0] < end[0] and start[1] == end[1]: + arrow.flip(m.RIGHT).shift(m.UP) commitId, commitMessage, commit, hide_refs = self.build_commit_id_and_message( - commit, dots + commit, i ) commitId.next_to(circle, m.UP) @@ -231,7 +273,7 @@ def draw_commit( color=self.fontColor, ).next_to(circle, m.DOWN) - if settings.animate and commit != "dark" and not self.stop: + if settings.animate and commit != "dark" and isNewCommit: self.play( self.camera.frame.animate.move_to(circle.get_center()), m.Create(circle), @@ -239,7 +281,7 @@ def draw_commit( m.AddTextLetterByLetter(message), run_time=1 / settings.speed, ) - elif not self.stop: + elif isNewCommit: self.add(circle, commitId, message) else: return commitId, circle, arrow, hide_refs @@ -252,7 +294,17 @@ def draw_commit( return commitId, circle, arrow, hide_refs - def build_commit_id_and_message(self, commit, dots=False): + def get_nonparent_branch_names(self): + branches = [b for b in self.repo.heads if not b.name.startswith("remotes/")] + exclude = [] + for b1 in branches: + for b2 in branches: + if b1.name != b2.name: + if self.repo.is_ancestor(b1.commit, b2.commit): + exclude.append(b1.name) + return [b for b in branches if b.name not in exclude] + + def build_commit_id_and_message(self, commit, i, dots=False): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) @@ -276,7 +328,7 @@ def build_commit_id_and_message(self, commit, dots=False): commitMessage = commit.message.split("\n")[0][:40].replace("\n", " ") return commitId, commitMessage, commit, hide_refs - def draw_head(self, commit, commitId): + def draw_head(self, commit, i, commitId): if commit.hexsha == self.repo.head.commit.hexsha: headbox = m.Rectangle(color=m.BLUE, fill_color=m.BLUE, fill_opacity=0.25) headbox.width = 1 @@ -297,10 +349,10 @@ def draw_head(self, commit, commitId): self.drawnRefs["HEAD"] = head self.prevRef = head - if self.i == 0: + if i == 0: self.topref = self.prevRef - def draw_branch(self, commit): + def draw_branch(self, commit, i): x = 0 remote_tracking_branches = self.get_remote_tracking_branches() @@ -348,17 +400,17 @@ def draw_branch(self, commit): self.toFadeOut.add(branchRec, branchText) self.drawnRefs[branch] = fullbranch - if self.i == 0: + if i == 0: self.topref = self.prevRef x += 1 if x >= settings.max_branches_per_commit: return - def draw_tag(self, commit): + def draw_tag(self, commit, i): x = 0 - if settings.hide_first_tag and self.i == 0: + if settings.hide_first_tag and i == 0: return for tag in self.repo.tags: @@ -394,7 +446,7 @@ def draw_tag(self, commit): self.toFadeOut.add(tagRec, tagText) - if self.i == 0: + if i == 0: self.topref = self.prevRef x += 1 @@ -878,7 +930,7 @@ def get_nondark_commits(self): nondark_commits.append(commit) return nondark_commits - def draw_ref(self, commit, top, text="HEAD", color=m.BLUE): + def draw_ref(self, commit, i, top, text="HEAD", color=m.BLUE): refText = m.Text(text, font="Monospace", font_size=20, color=self.fontColor) refbox = m.Rectangle( color=color, @@ -901,7 +953,7 @@ def draw_ref(self, commit, top, text="HEAD", color=m.BLUE): self.drawnRefs[text] = ref self.prevRef = ref - if self.i == 0: + if i == 0: self.topref = self.prevRef def draw_dark_ref(self): diff --git a/git_sim/log.py b/git_sim/log.py index 0df25a8..3d88fc9 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -16,7 +16,6 @@ def __init__(self, commits: int, all: bool): self.selected_branches.append(self.repo.active_branch.name) except TypeError: pass - self.arrow_map = [] self.all = all def construct(self): @@ -36,167 +35,6 @@ def construct(self): self.fadeout() self.show_outro() - def parse_commits( - self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]) - ): - isNewCommit = commit.hexsha not in self.drawnCommits - - if i < self.numCommits and commit in self.commits: - commitId, circle, arrow, hide_refs = self.draw_commit( - commit, prevCircle, shift - ) - - if commit != "dark": - if not hide_refs and isNewCommit: - self.draw_head(commit, commitId) - self.draw_branch(commit) - self.draw_tag(commit) - if ( - not isinstance(arrow, m.CurvedArrow) - and [arrow.start.tolist(), arrow.end.tolist()] not in self.arrow_map - ): - self.draw_arrow(prevCircle, arrow) - self.arrow_map.append([arrow.start.tolist(), arrow.end.tolist()]) - elif ( - isinstance(arrow, m.CurvedArrow) - and [arrow.get_start().tolist(), arrow.get_end().tolist()] - not in self.arrow_map - ): - self.draw_arrow(prevCircle, arrow) - self.arrow_map.append( - [arrow.get_start().tolist(), arrow.get_end().tolist()] - ) - if i == 0 and len(self.drawnRefs) < 2: - self.draw_dark_ref() - - if i < self.numCommits: # len(self.commits) - 1: - i += 1 - commitParents = list(commit.parents) - if len(commitParents) > 0: - if settings.invert_branches: - commitParents.reverse() - - if settings.hide_merged_chains: - self.parse_commits(commitParents[0], i, circle) - else: - for p in range(len(commitParents)): - self.parse_commits(commitParents[p], i, circle) - - def draw_commit(self, commit, prevCircle, shift=numpy.array([0.0, 0.0, 0.0])): - if commit == "dark": - commitFill = m.WHITE if settings.light_mode else m.BLACK - elif len(commit.parents) <= 1: - commitFill = m.RED - else: - commitFill = m.GRAY - - circle = m.Circle( - stroke_color=commitFill, fill_color=commitFill, fill_opacity=0.25 - ) - circle.height = 1 - - if shift.any(): - circle.shift(shift) - - if prevCircle: - circle.next_to( - prevCircle, m.RIGHT if settings.reverse else m.LEFT, buff=1.5 - ) - - while any((circle.get_center() == c).all() for c in self.get_centers()): - circle.next_to(circle, m.DOWN, buff=3.5) - - isNewCommit = commit.hexsha not in self.drawnCommits - - if isNewCommit: - start = ( - prevCircle.get_center() - if prevCircle - else (m.LEFT if settings.reverse else m.RIGHT) - ) - end = circle.get_center() - else: - circle.move_to(self.drawnCommits[commit.hexsha].get_center()) - start = ( - prevCircle.get_center() - if prevCircle - else (m.LEFT if settings.reverse else m.RIGHT) - ) - end = self.drawnCommits[commit.hexsha].get_center() - - arrow = m.Arrow(start, end, color=self.fontColor) - - if commit == "dark": - arrow = m.Arrow( - start, end, color=m.WHITE if settings.light_mode else m.BLACK - ) - - length = numpy.linalg.norm(start - end) - (1.5 if start[1] == end[1] else 3) - arrow.set_length(length) - angle = arrow.get_angle() - lineRect = ( - m.Rectangle(height=0.1, width=length, color="#123456") - .move_to(arrow.get_center()) - .rotate(angle) - ) - - for commitCircle in self.drawnCommits.values(): - inter = m.Intersection(lineRect, commitCircle) - if inter.has_points(): - arrow = m.CurvedArrow(start, end, color=self.fontColor) - if start[1] == end[1]: - arrow.shift(m.UP * 1.25) - if start[0] < end[0] and start[1] == end[1]: - arrow.flip(m.RIGHT).shift(m.UP) - - commitId, commitMessage, commit, hide_refs = self.build_commit_id_and_message( - commit - ) - commitId.next_to(circle, m.UP) - - if commit != "dark": - self.drawnCommitIds[commit.hexsha] = commitId - - message = m.Text( - "\n".join( - commitMessage[j : j + 20] for j in range(0, len(commitMessage), 20) - )[:100], - font="Monospace", - font_size=14, - color=self.fontColor, - ).next_to(circle, m.DOWN) - - if settings.animate and commit != "dark" and isNewCommit: - self.play( - self.camera.frame.animate.move_to(circle.get_center()), - m.Create(circle), - m.AddTextLetterByLetter(commitId), - m.AddTextLetterByLetter(message), - run_time=1 / settings.speed, - ) - elif isNewCommit: - self.add(circle, commitId, message) - else: - return commitId, circle, arrow, hide_refs - - if commit != "dark": - self.drawnCommits[commit.hexsha] = circle - - self.toFadeOut.add(circle, commitId, message) - self.prevRef = commitId - - return commitId, circle, arrow, hide_refs - - def get_nonparent_branch_names(self): - branches = [b for b in self.repo.heads if not b.name.startswith("remotes/")] - exclude = [] - for b1 in branches: - for b2 in branches: - if b1.name != b2.name: - if self.repo.is_ancestor(b1.commit, b2.commit): - exclude.append(b1.name) - return [b for b in branches if b.name not in exclude] - def log( commits: int = typer.Option( diff --git a/git_sim/merge.py b/git_sim/merge.py index 95fa898..e897db5 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -73,7 +73,7 @@ def construct(self): if self.ff: self.get_commits(start=self.branch) - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) reset_head_to = self.commits[0].hexsha shift = numpy.array([0.0, 0.6, 0.0]) @@ -98,10 +98,9 @@ def construct(self): else: self.get_commits() - self.parse_commits(self.commits[0]) - self.i = 0 + self.parse_commits(self.commits[0], 0) self.get_commits(start=self.branch) - self.parse_commits(self.commits[0], shift=4 * m.DOWN) + self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) self.center_frame_on_commit(self.orig_commits[0]) self.setup_and_draw_parent( self.orig_commits[0], diff --git a/git_sim/rebase.py b/git_sim/rebase.py index e1d3f26..9d37892 100644 --- a/git_sim/rebase.py +++ b/git_sim/rebase.py @@ -65,9 +65,8 @@ def construct(self): self.show_intro() self.get_commits(start=self.branch) - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.orig_commits = self.commits - self.i = 0 self.get_commits() reached_base = False @@ -77,9 +76,7 @@ def construct(self): ): reached_base = True - self.parse_commits( - self.commits[0], shift=4 * m.DOWN, dots=False if reached_base else True - ) + self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) self.center_frame_on_commit(self.orig_commits[0]) to_rebase = [] diff --git a/git_sim/reset.py b/git_sim/reset.py index 1522f91..27b27cf 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -24,6 +24,7 @@ def __init__( super().__init__() self.commit = commit self.mode = mode + settings.hide_merged_chains = True try: self.resetTo = git.repo.fun.rev_parse(self.repo, self.commit) @@ -56,7 +57,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[self.i]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.reset_head_branch(self.resetTo.hexsha) @@ -65,12 +66,12 @@ def construct(self): self.fadeout() self.show_outro() - def build_commit_id_and_message(self, commit, dots=False): + def build_commit_id_and_message(self, commit, i, dots=False): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) commitMessage = "" - elif self.i == 3 and self.resetTo.hexsha not in [ + elif i == 3 and self.resetTo.hexsha not in [ commit.hexsha for commit in self.get_nondark_commits() ]: commitId = m.Text( @@ -78,7 +79,7 @@ def build_commit_id_and_message(self, commit, dots=False): ) commitMessage = "..." hide_refs = True - elif self.i == 4 and self.resetTo.hexsha not in [ + elif i == 4 and self.resetTo.hexsha not in [ commit.hexsha for commit in self.get_nondark_commits() ]: commitId = m.Text( diff --git a/git_sim/restore.py b/git_sim/restore.py index b921124..932afb0 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -14,6 +14,7 @@ def __init__(self, files: List[str]): super().__init__() self.hide_first_tag = True self.files = files + settings.hide_merged_chains = True try: self.selected_branches.append(self.repo.active_branch.name) @@ -35,7 +36,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/revert.py b/git_sim/revert.py index b16af1a..f70c057 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -43,7 +43,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[self.i]) + self.parse_commits(self.commits[0], 0) self.center_frame_on_commit(self.commits[0]) self.setup_and_draw_revert_commit() self.recenter_frame() @@ -58,12 +58,12 @@ def construct(self): self.fadeout() self.show_outro() - def build_commit_id_and_message(self, commit, dots=False): + def build_commit_id_and_message(self, commit, i, dots=False): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) commitMessage = "" - elif self.i == 2 and self.revert.hexsha not in [ + elif i == 2 and self.revert.hexsha not in [ commit.hexsha for commit in self.commits ]: commitId = m.Text( @@ -71,7 +71,7 @@ def build_commit_id_and_message(self, commit, dots=False): ) commitMessage = "..." hide_refs = True - elif self.i == 3 and self.revert.hexsha not in [ + elif i == 3 and self.revert.hexsha not in [ commit.hexsha for commit in self.commits ]: commitId = m.Text( diff --git a/git_sim/stash.py b/git_sim/stash.py index 47e75a0..e5861c0 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -23,6 +23,7 @@ def __init__(self, files: List[str], command: StashSubCommand): self.files = files self.no_files = True if not self.files else False self.command = command + settings.hide_merged_chains = True try: self.selected_branches.append(self.repo.active_branch.name) @@ -57,7 +58,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/status.py b/git_sim/status.py index 0aa4e8f..7fe21f9 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -10,13 +10,14 @@ def __init__(self): self.selected_branches.append(self.repo.active_branch.name) except TypeError: pass + settings.hide_merged_chains = True def construct(self): if not settings.stdout: print(f"{settings.INFO_STRING } {type(self).__name__.lower()}") self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/tag.py b/git_sim/tag.py index cbfa058..2a48117 100644 --- a/git_sim/tag.py +++ b/git_sim/tag.py @@ -17,7 +17,7 @@ def construct(self): self.show_intro() self.get_commits() - self.parse_commits(self.commits[0]) + self.parse_commits(self.commits[0], 0) self.recenter_frame() self.scale_frame() From 1ca2d70a8af5fb4a08bdd560c9aee853ef3494d9 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Tue, 28 Feb 2023 23:22:46 -0800 Subject: [PATCH 11/19] Refactor numCommits variable to a more general n Signed-off-by: Jacob Stopak --- git_sim/__main__.py | 6 ++++++ git_sim/commit.py | 5 +++-- git_sim/git_sim_base_command.py | 27 +++++++++++++-------------- git_sim/log.py | 26 ++++++++++++++++++-------- git_sim/revert.py | 5 +++-- git_sim/settings.py | 4 +++- 6 files changed, 46 insertions(+), 27 deletions(-) diff --git a/git_sim/__main__.py b/git_sim/__main__.py index e842acf..74f0f95 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -32,6 +32,11 @@ def main( settings.animate, help="Animate the simulation and output as an mp4 video", ), + n: int = typer.Option( + settings.n, + "-n", + help="Number of commits to display from each branch head", + ), auto_open: bool = typer.Option( settings.auto_open, "--auto-open", @@ -117,6 +122,7 @@ def main( ), ): settings.animate = animate + settings.n = n settings.auto_open = auto_open settings.img_format = img_format settings.light_mode = light_mode diff --git a/git_sim/commit.py b/git_sim/commit.py index ac5c1df..03cfa66 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -15,8 +15,9 @@ def __init__(self, message: str, amend: bool): self.message = message self.amend = amend - self.defaultNumCommits = 4 if not self.amend else 5 - self.numCommits = 4 if not self.amend else 5 + self.n_default = 4 if not self.amend else 5 + self.n = self.n_default + self.hide_first_tag = True settings.hide_merged_chains = True diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index f7587d9..9f4eed5 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -25,8 +25,9 @@ def __init__(self): self.trimmed = False self.prevRef = None self.topref = None - self.numCommits = settings.commits - self.defaultNumCommits = settings.commits + self.n_default = settings.n_default + self.n = settings.n + self.n_orig = self.n self.selected_branches = [] self.stop = False self.zone_title_offset = 2.6 if platform.system() == "Windows" else 2.6 @@ -50,9 +51,9 @@ def construct(self): self.show_outro() def get_commits(self, start="HEAD"): - if not self.numCommits: + if not self.n: if settings.allow_no_commits: - self.numCommits = self.defaultNumCommits + self.n = self.n_default self.commits = ["dark"] * 5 self.zone_title_offset = 2 return @@ -63,21 +64,19 @@ def get_commits(self, start="HEAD"): try: self.commits = ( list(self.repo.iter_commits(start)) - if self.numCommits == 1 + if self.n == 1 else list( - self.repo.iter_commits( - start + "~" + str(self.numCommits) + "..." + start - ) + self.repo.iter_commits(start + "~" + str(self.n) + "..." + start) ) ) - if len(self.commits) < self.defaultNumCommits: + if len(self.commits) < self.n_default: self.commits = list(self.repo.iter_commits(start)) - while len(self.commits) < self.defaultNumCommits: + while len(self.commits) < self.n_default: self.commits.append(self.create_dark_commit()) - self.numCommits = self.defaultNumCommits + self.n = self.n_orig except GitCommandError: - self.numCommits -= 1 + self.n -= 1 self.get_commits(start=start) def parse_commits( @@ -85,7 +84,7 @@ def parse_commits( ): isNewCommit = commit.hexsha not in self.drawnCommits - if i < self.numCommits and commit in self.commits: + if i < self.n and commit in self.commits: commitId, circle, arrow, hide_refs = self.draw_commit( commit, i, prevCircle, shift ) @@ -113,7 +112,7 @@ def parse_commits( if i == 0 and len(self.drawnRefs) < 2: self.draw_dark_ref() - if i < self.numCommits: # len(self.commits) - 1: + if i < self.n: i += 1 commitParents = list(commit.parents) if len(commitParents) > 0: diff --git a/git_sim/log.py b/git_sim/log.py index 3d88fc9..bea575a 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -8,10 +8,18 @@ class Log(GitSimBaseCommand): - def __init__(self, commits: int, all: bool): + def __init__(self, ctx: typer.Context, n: int, all: bool): super().__init__() - self.numCommits = commits - self.defaultNumCommits = commits + + n_command = ctx.parent.params.get("n") + n_subcommand = n + if n_subcommand: + n = n_subcommand + else: + n = n_command + self.n = n + self.n_orig = self.n + try: self.selected_branches.append(self.repo.active_branch.name) except TypeError: @@ -37,15 +45,17 @@ def construct(self): def log( - commits: int = typer.Option( - default=settings.commits, - help="The number of commits to display in the simulated log output", + ctx: typer.Context, + n: int = typer.Option( + settings.n_subcommand, + "-n", + help="Number of commits to display from branch heads", min=1, ), all: bool = typer.Option( - default=False, + False, help="Display all local branches in the log output", ), ): - scene = Log(commits=commits, all=all) + scene = Log(ctx=ctx, n=n, all=all) handle_animations(scene=scene) diff --git a/git_sim/revert.py b/git_sim/revert.py index f70c057..e3f7aaf 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -25,8 +25,9 @@ def __init__(self, commit: str): ) sys.exit(1) - self.defaultNumCommits = 4 - self.numCommits = 4 + self.n_default = 4 + self.n = self.n_default + self.hide_first_tag = True self.zone_title_offset += 0.1 diff --git a/git_sim/settings.py b/git_sim/settings.py index 2ecf3aa..97dcee0 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -19,7 +19,9 @@ class Settings(BaseSettings): allow_no_commits = False animate = False auto_open = True - commits = 5 + n_default = 5 + n = 5 + n_subcommand: Union[int, None] = None files: Union[List[pathlib.Path], None] = None hide_first_tag = False img_format: ImgFormat = ImgFormat.jpg From 82f51f0cb1cc03b415690439ad6b563749788f9c Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 06:21:32 -0800 Subject: [PATCH 12/19] Consolidate get_commits and parse_commits methods Signed-off-by: Jacob Stopak --- git_sim/add.py | 3 +- git_sim/branch.py | 3 +- git_sim/cherrypick.py | 17 +++++---- git_sim/commit.py | 14 ++++---- git_sim/git_sim_base_command.py | 61 +++++++++------------------------ git_sim/log.py | 14 ++++---- git_sim/merge.py | 38 +++++++++----------- git_sim/rebase.py | 21 ++++++------ git_sim/reset.py | 9 +++-- git_sim/restore.py | 3 +- git_sim/revert.py | 16 ++++----- git_sim/stash.py | 3 +- git_sim/status.py | 3 +- git_sim/tag.py | 3 +- 14 files changed, 82 insertions(+), 126 deletions(-) diff --git a/git_sim/add.py b/git_sim/add.py index 75e5a43..6508796 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -37,8 +37,7 @@ def construct(self): ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/branch.py b/git_sim/branch.py index 068c015..31f5f13 100644 --- a/git_sim/branch.py +++ b/git_sim/branch.py @@ -16,8 +16,7 @@ def construct(self): print(f"{settings.INFO_STRING} {type(self).__name__.lower()} {self.name}") self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() diff --git a/git_sim/cherrypick.py b/git_sim/cherrypick.py index 6667868..c079599 100644 --- a/git_sim/cherrypick.py +++ b/git_sim/cherrypick.py @@ -53,17 +53,16 @@ def construct(self): sys.exit(1) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) - self.orig_commits = self.commits - self.get_commits(start=self.commit) - self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) - self.center_frame_on_commit(self.orig_commits[0]) + head_commit = self.get_commit() + self.parse_commits(head_commit) + cherry_picked_commit = self.get_commit(self.commit) + self.parse_commits(cherry_picked_commit, shift=4 * m.DOWN) + self.center_frame_on_commit(head_commit) self.setup_and_draw_parent( - self.orig_commits[0], - self.edit if self.edit else self.commits[0].message, + head_commit, + self.edit if self.edit else cherry_picked_commit.message, ) - self.draw_arrow_between_commits(self.commits[0].hexsha, "abcdef") + self.draw_arrow_between_commits(cherry_picked_commit.hexsha, "abcdef") self.recenter_frame() self.scale_frame() self.reset_head_branch("abcdef") diff --git a/git_sim/commit.py b/git_sim/commit.py index 03cfa66..06941bc 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -42,7 +42,7 @@ def construct(self): ) self.show_intro() - self.get_commits() + head_commit = self.get_commit() if self.amend: tree = self.repo.tree() @@ -51,17 +51,17 @@ def construct(self): tree, self.message, ) - self.commits[0] = amended + head_commit = amended - self.parse_commits(self.commits[0], 0) - self.center_frame_on_commit(self.commits[0]) + self.parse_commits(head_commit) + self.center_frame_on_commit(head_commit) if not self.amend: - self.setup_and_draw_parent(self.commits[0], self.message) + self.setup_and_draw_parent(head_commit, self.message) else: - self.draw_ref(self.commits[0], self.drawnCommitIds[amended.hexsha]) + self.draw_ref(head_commit, self.drawnCommitIds[amended.hexsha]) self.draw_ref( - self.commits[0], + head_commit, self.drawnRefs["HEAD"], text=self.repo.active_branch.name, color=m.GREEN, diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index 9f4eed5..0b9c2ce 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -19,7 +19,6 @@ def __init__(self): self.drawnCommits = {} self.drawnRefs = {} self.drawnCommitIds = {} - self.commits = [] self.zoomOuts = 0 self.toFadeOut = m.Group() self.trimmed = False @@ -46,45 +45,31 @@ def init_repo(self): def construct(self): print(f"{settings.INFO_STRING} {type(self).__name__.lower()}") self.show_intro() - self.get_commits() + self.parse_commits() self.fadeout() self.show_outro() - def get_commits(self, start="HEAD"): - if not self.n: - if settings.allow_no_commits: - self.n = self.n_default - self.commits = ["dark"] * 5 - self.zone_title_offset = 2 - return - else: - print("git-sim error: No commits in current Git repository.") - sys.exit(1) - - try: - self.commits = ( - list(self.repo.iter_commits(start)) - if self.n == 1 - else list( - self.repo.iter_commits(start + "~" + str(self.n) + "..." + start) - ) - ) - if len(self.commits) < self.n_default: - self.commits = list(self.repo.iter_commits(start)) - while len(self.commits) < self.n_default: - self.commits.append(self.create_dark_commit()) - self.n = self.n_orig + def get_commit(self, sha_or_ref="HEAD"): + return self.repo.commit(sha_or_ref) - except GitCommandError: - self.n -= 1 - self.get_commits(start=start) + def get_default_commits(self): + defaultCommits = [self.get_commit()] + for x in range(self.n_default - 1): + defaultCommits.append(defaultCommits[-1].parents[0]) + return defaultCommits def parse_commits( - self, commit, i, prevCircle=None, shift=numpy.array([0.0, 0.0, 0.0]) + self, + commit=None, + i=0, + prevCircle=None, + shift=numpy.array([0.0, 0.0, 0.0]), ): + commit = commit or self.get_commit() + isNewCommit = commit.hexsha not in self.drawnCommits - if i < self.n and commit in self.commits: + if i < self.n: commitId, circle, arrow, hide_refs = self.draw_commit( commit, i, prevCircle, shift ) @@ -303,20 +288,11 @@ def get_nonparent_branch_names(self): exclude.append(b1.name) return [b for b in branches if b.name not in exclude] - def build_commit_id_and_message(self, commit, i, dots=False): + def build_commit_id_and_message(self, commit, i): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) commitMessage = "" - elif ( - dots - and self.commits[-1] != "dark" - and commit.hexsha == self.commits[-1].hexsha - ): - commitId = m.Text( - "...", font="Monospace", font_size=20, color=self.fontColor - ) - commitMessage = "..." else: commitId = m.Text( commit.hexsha[0:6], @@ -924,9 +900,6 @@ def create_dark_commit(self): def get_nondark_commits(self): nondark_commits = [] - for commit in self.commits: - if commit != "dark": - nondark_commits.append(commit) return nondark_commits def draw_ref(self, commit, i, top, text="HEAD", color=m.BLUE): diff --git a/git_sim/log.py b/git_sim/log.py index bea575a..8cb8cf4 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -12,9 +12,9 @@ def __init__(self, ctx: typer.Context, n: int, all: bool): super().__init__() n_command = ctx.parent.params.get("n") - n_subcommand = n - if n_subcommand: - n = n_subcommand + self.n_subcommand = n + if self.n_subcommand: + n = self.n_subcommand else: n = n_command self.n = n @@ -29,15 +29,13 @@ def __init__(self, ctx: typer.Context, n: int, all: bool): def construct(self): if not settings.stdout: print( - f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all else ''}" + f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all else ''}{' -n ' + str(self.n) if self.n_subcommand else ''}" ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() if self.all: for branch in self.get_nonparent_branch_names(): - self.get_commits(start=branch.name) - self.parse_commits(self.commits[0], 0) + self.parse_commits(self.get_commit(branch.name)) self.recenter_frame() self.scale_frame() self.fadeout() diff --git a/git_sim/merge.py b/git_sim/merge.py index e897db5..0aaf9aa 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -55,31 +55,27 @@ def construct(self): sys.exit(1) self.show_intro() - self.get_commits() - self.orig_commits = self.commits - self.get_commits(start=self.branch) + head_commit = self.get_commit() + branch_commit = self.get_commit(self.branch) # Use forward slash to determine if supplied branch arg is local or remote tracking branch if not self.is_remote_tracking_branch(self.branch): - if self.branch in self.repo.git.branch( - "--contains", self.orig_commits[0].hexsha - ): + if self.branch in self.repo.git.branch("--contains", head_commit.hexsha): self.ff = True else: if self.branch in self.repo.git.branch( - "-r", "--contains", self.orig_commits[0].hexsha + "-r", "--contains", head_commit.hexsha ): self.ff = True if self.ff: - self.get_commits(start=self.branch) - self.parse_commits(self.commits[0], 0) - reset_head_to = self.commits[0].hexsha + self.parse_commits(branch_commit) + reset_head_to = branch_commit.hexsha shift = numpy.array([0.0, 0.6, 0.0]) if self.no_ff: - self.center_frame_on_commit(self.commits[0]) - commitId = self.setup_and_draw_parent(self.commits[0], "Merge commit") + self.center_frame_on_commit(branch_commit) + commitId = self.setup_and_draw_parent(branch_commit, "Merge commit") reset_head_to = "abcdef" shift = numpy.array([0.0, 0.0, 0.0]) @@ -88,29 +84,27 @@ def construct(self): if "HEAD" in self.drawnRefs: self.reset_head_branch(reset_head_to, shift=shift) else: - self.draw_ref(self.commits[0], commitId if self.no_ff else self.topref) + self.draw_ref(branch_commit, commitId if self.no_ff else self.topref) self.draw_ref( - self.commits[0], + branch_commit, self.drawnRefs["HEAD"], text=self.repo.active_branch.name, color=m.GREEN, ) else: - self.get_commits() - self.parse_commits(self.commits[0], 0) - self.get_commits(start=self.branch) - self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) - self.center_frame_on_commit(self.orig_commits[0]) + self.parse_commits(head_commit) + self.parse_commits(branch_commit, shift=4 * m.DOWN) + self.center_frame_on_commit(head_commit) self.setup_and_draw_parent( - self.orig_commits[0], + head_commit, "Merge commit", shift=2 * m.DOWN, draw_arrow=False, color=m.GRAY, ) - self.draw_arrow_between_commits("abcdef", self.commits[0].hexsha) - self.draw_arrow_between_commits("abcdef", self.orig_commits[0].hexsha) + self.draw_arrow_between_commits("abcdef", branch_commit.hexsha) + self.draw_arrow_between_commits("abcdef", head_commit.hexsha) self.recenter_frame() self.scale_frame() self.reset_head_branch("abcdef") diff --git a/git_sim/rebase.py b/git_sim/rebase.py index 9d37892..c8b40bc 100644 --- a/git_sim/rebase.py +++ b/git_sim/rebase.py @@ -64,32 +64,31 @@ def construct(self): sys.exit(1) self.show_intro() - self.get_commits(start=self.branch) - self.parse_commits(self.commits[0], 0) - self.orig_commits = self.commits - self.get_commits() + branch_commit = self.get_commit(self.branch) + self.parse_commits(branch_commit) + head_commit = self.get_commit() reached_base = False - for commit in self.commits: + for commit in self.get_default_commits(): if commit != "dark" and self.branch in self.repo.git.branch( "--contains", commit ): reached_base = True - self.parse_commits(self.commits[0], 0, shift=4 * m.DOWN) - self.center_frame_on_commit(self.orig_commits[0]) + self.parse_commits(head_commit, shift=4 * m.DOWN) + self.center_frame_on_commit(branch_commit) to_rebase = [] i = 0 - current = self.commits[i] + current = head_commit while self.branch not in self.repo.git.branch("--contains", current): to_rebase.append(current) i += 1 - if i >= len(self.commits): + if i >= self.n: break - current = self.commits[i] + current = self.get_default_commits()[i] - parent = self.orig_commits[0].hexsha + parent = branch_commit.hexsha for j, tr in enumerate(reversed(to_rebase)): if not reached_base and j == 0: diff --git a/git_sim/reset.py b/git_sim/reset.py index 27b27cf..16edde9 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -56,8 +56,7 @@ def construct(self): ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() self.reset_head_branch(self.resetTo.hexsha) @@ -66,13 +65,13 @@ def construct(self): self.fadeout() self.show_outro() - def build_commit_id_and_message(self, commit, i, dots=False): + def build_commit_id_and_message(self, commit, i): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) commitMessage = "" elif i == 3 and self.resetTo.hexsha not in [ - commit.hexsha for commit in self.get_nondark_commits() + c.hexsha for c in self.get_default_commits() ]: commitId = m.Text( "...", font="Monospace", font_size=20, color=self.fontColor @@ -80,7 +79,7 @@ def build_commit_id_and_message(self, commit, i, dots=False): commitMessage = "..." hide_refs = True elif i == 4 and self.resetTo.hexsha not in [ - commit.hexsha for commit in self.get_nondark_commits() + c.hexsha for c in self.get_default_commits() ]: commitId = m.Text( self.resetTo.hexsha[:6], diff --git a/git_sim/restore.py b/git_sim/restore.py index 932afb0..befc87b 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -35,8 +35,7 @@ def construct(self): ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/revert.py b/git_sim/revert.py index e3f7aaf..6e5dafe 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -27,6 +27,7 @@ def __init__(self, commit: str): self.n_default = 4 self.n = self.n_default + settings.hide_merged_chains = True self.hide_first_tag = True self.zone_title_offset += 0.1 @@ -43,9 +44,8 @@ def construct(self): ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) - self.center_frame_on_commit(self.commits[0]) + self.parse_commits() + self.center_frame_on_commit(self.get_commit()) self.setup_and_draw_revert_commit() self.recenter_frame() self.scale_frame() @@ -59,13 +59,13 @@ def construct(self): self.fadeout() self.show_outro() - def build_commit_id_and_message(self, commit, i, dots=False): + def build_commit_id_and_message(self, commit, i): hide_refs = False if commit == "dark": commitId = m.Text("", font="Monospace", font_size=20, color=self.fontColor) commitMessage = "" elif i == 2 and self.revert.hexsha not in [ - commit.hexsha for commit in self.commits + commit.hexsha for commit in self.get_default_commits() ]: commitId = m.Text( "...", font="Monospace", font_size=20, color=self.fontColor @@ -73,7 +73,7 @@ def build_commit_id_and_message(self, commit, i, dots=False): commitMessage = "..." hide_refs = True elif i == 3 and self.revert.hexsha not in [ - commit.hexsha for commit in self.commits + commit.hexsha for commit in self.get_default_commits() ]: commitId = m.Text( self.revert.hexsha[:6], @@ -97,13 +97,13 @@ def setup_and_draw_revert_commit(self): circle = m.Circle(stroke_color=m.RED, fill_color=m.RED, fill_opacity=0.25) circle.height = 1 circle.next_to( - self.drawnCommits[self.commits[0].hexsha], + self.drawnCommits[self.get_commit().hexsha], m.LEFT if settings.reverse else m.RIGHT, buff=1.5, ) start = circle.get_center() - end = self.drawnCommits[self.commits[0].hexsha].get_center() + end = self.drawnCommits[self.get_commit().hexsha].get_center() arrow = m.Arrow(start, end, color=self.fontColor) length = numpy.linalg.norm(start - end) - (1.5 if start[1] == end[1] else 3) arrow.set_length(length) diff --git a/git_sim/stash.py b/git_sim/stash.py index e5861c0..2077738 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -57,8 +57,7 @@ def construct(self): ) self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/status.py b/git_sim/status.py index 7fe21f9..9c79294 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -16,8 +16,7 @@ def construct(self): if not settings.stdout: print(f"{settings.INFO_STRING } {type(self).__name__.lower()}") self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() self.vsplit_frame() diff --git a/git_sim/tag.py b/git_sim/tag.py index 2a48117..58de08c 100644 --- a/git_sim/tag.py +++ b/git_sim/tag.py @@ -16,8 +16,7 @@ def construct(self): print(f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.name}") self.show_intro() - self.get_commits() - self.parse_commits(self.commits[0], 0) + self.parse_commits() self.recenter_frame() self.scale_frame() From b900b304ee2a668eb3d0f3fe62d8633bff856ecf Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 06:34:20 -0800 Subject: [PATCH 13/19] Rename --hide-merged-chains to --hide-merged-branches Signed-off-by: Jacob Stopak --- README.md | 2 +- git_sim/__main__.py | 6 +++--- git_sim/add.py | 2 +- git_sim/commit.py | 2 +- git_sim/git_sim_base_command.py | 2 +- git_sim/reset.py | 2 +- git_sim/restore.py | 2 +- git_sim/revert.py | 2 +- git_sim/settings.py | 2 +- git_sim/stash.py | 2 +- git_sim/status.py | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 59650b3..e2d947b 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ The `[global options]` apply to the overarching `git-sim` simulation itself, inc `--img-format`: Output format for the image file, i.e. `jpg` or `png`. Default output format is `jpg`. `--stdout`: Write raw image data to stdout while suppressing all other program output. `--invert-branches`: Invert positioning of branches by reversing order of multiple parents where applicable. -`--hide-merged-chains`: Hide commits from merged branches, i.e. only display mainline commits. +`--hide-merged-branches`: Hide commits from merged branches, i.e. only display mainline commits. Animation-only global options (to be used in conjunction with `--animate`): diff --git a/git_sim/__main__.py b/git_sim/__main__.py index 74f0f95..8a7af92 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -116,8 +116,8 @@ def main( settings.invert_branches, help="Invert positioning of branches by reversing order of multiple parents where applicable", ), - hide_merged_chains: bool = typer.Option( - settings.hide_merged_chains, + hide_merged_branches: bool = typer.Option( + settings.hide_merged_branches, help="Hide commits from merged branches, i.e. only display mainline commits", ), ): @@ -141,7 +141,7 @@ def main( settings.video_format = video_format settings.stdout = stdout settings.invert_branches = invert_branches - settings.hide_merged_chains = hide_merged_chains + settings.hide_merged_branches = hide_merged_branches if sys.platform == "linux" or sys.platform == "darwin": repo_name = git.repo.Repo( diff --git a/git_sim/add.py b/git_sim/add.py index 6508796..d63a85e 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -16,7 +16,7 @@ def __init__(self, files: List[str]): self.hide_first_tag = True self.allow_no_commits = True self.files = files - settings.hide_merged_chains = True + settings.hide_merged_branches = True try: self.selected_branches.append(self.repo.active_branch.name) diff --git a/git_sim/commit.py b/git_sim/commit.py index 06941bc..62747d3 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -19,7 +19,7 @@ def __init__(self, message: str, amend: bool): self.n = self.n_default self.hide_first_tag = True - settings.hide_merged_chains = True + settings.hide_merged_branches = True try: self.selected_branches.append(self.repo.active_branch.name) diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index 0b9c2ce..abcb692 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -104,7 +104,7 @@ def parse_commits( if settings.invert_branches: commitParents.reverse() - if settings.hide_merged_chains: + if settings.hide_merged_branches: self.parse_commits(commitParents[0], i, circle) else: for p in range(len(commitParents)): diff --git a/git_sim/reset.py b/git_sim/reset.py index 16edde9..a10c5a0 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -24,7 +24,7 @@ def __init__( super().__init__() self.commit = commit self.mode = mode - settings.hide_merged_chains = True + settings.hide_merged_branches = True try: self.resetTo = git.repo.fun.rev_parse(self.repo, self.commit) diff --git a/git_sim/restore.py b/git_sim/restore.py index befc87b..0df780d 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -14,7 +14,7 @@ def __init__(self, files: List[str]): super().__init__() self.hide_first_tag = True self.files = files - settings.hide_merged_chains = True + settings.hide_merged_branches = True try: self.selected_branches.append(self.repo.active_branch.name) diff --git a/git_sim/revert.py b/git_sim/revert.py index 6e5dafe..79334cd 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -27,7 +27,7 @@ def __init__(self, commit: str): self.n_default = 4 self.n = self.n_default - settings.hide_merged_chains = True + settings.hide_merged_branches = True self.hide_first_tag = True self.zone_title_offset += 0.1 diff --git a/git_sim/settings.py b/git_sim/settings.py index 97dcee0..705f75e 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -42,7 +42,7 @@ class Settings(BaseSettings): video_format: VideoFormat = VideoFormat.mp4 stdout = False invert_branches = False - hide_merged_chains = False + hide_merged_branches = False class Config: env_prefix = "git_sim_" diff --git a/git_sim/stash.py b/git_sim/stash.py index 2077738..f468fc7 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -23,7 +23,7 @@ def __init__(self, files: List[str], command: StashSubCommand): self.files = files self.no_files = True if not self.files else False self.command = command - settings.hide_merged_chains = True + settings.hide_merged_branches = True try: self.selected_branches.append(self.repo.active_branch.name) diff --git a/git_sim/status.py b/git_sim/status.py index 9c79294..c0d0998 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -10,7 +10,7 @@ def __init__(self): self.selected_branches.append(self.repo.active_branch.name) except TypeError: pass - settings.hide_merged_chains = True + settings.hide_merged_branches = True def construct(self): if not settings.stdout: From 7fb6dc1e110029ee0e672be8a8cbfff8bd96719f Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 17:48:50 -0800 Subject: [PATCH 14/19] Extend --all as a global flag to subcommands that don't show table/zones Signed-off-by: Jacob Stopak --- README.md | 6 +++++- git_sim/__main__.py | 5 +++++ git_sim/branch.py | 6 ++++-- git_sim/cherrypick.py | 1 + git_sim/git_sim_base_command.py | 23 +++++++++++++---------- git_sim/log.py | 19 ++++++++++++------- git_sim/merge.py | 2 ++ git_sim/rebase.py | 1 + git_sim/settings.py | 2 +- git_sim/tag.py | 7 +++++-- 10 files changed, 49 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index e2d947b..b22c9ca 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,8 @@ $ git-sim [global options] [subcommand options] The `[global options]` apply to the overarching `git-sim` simulation itself, including: +`-n `: Number of commits to display from each branch head. +`--all`: Display all local branches in the log output. `--light-mode`: Use a light mode color scheme instead of default dark mode. `--animate`: Instead of outputting a static image, animate the Git command behavior in a .mp4 video. `--media-dir`: The path at which to store the simulated output media files. @@ -160,9 +162,11 @@ The `[subcommand options]` are like regular Git options specific to the specifie The following is a list of Git commands that can be simulated and their corresponding options/flags. ### git log -Usage: `git-sim log` +Usage: `git-sim log [-n ] [--all]` - Simulated output will show the most recent 5 commits on the active branch by default +- Use `-n ` to set number of commits to display from each branch head +- Set `--all` to display all local branches in the log output ![git-sim-log_01-05-23_22-02-39](https://user-images.githubusercontent.com/49353917/210940300-aadd14c6-72ab-4529-a1be-b494ed5dd4c9.jpg) diff --git a/git_sim/__main__.py b/git_sim/__main__.py index 8a7af92..cbbd60c 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -120,6 +120,10 @@ def main( settings.hide_merged_branches, help="Hide commits from merged branches, i.e. only display mainline commits", ), + all: bool = typer.Option( + settings.all, + help="Display all local branches in the log output", + ), ): settings.animate = animate settings.n = n @@ -142,6 +146,7 @@ def main( settings.stdout = stdout settings.invert_branches = invert_branches settings.hide_merged_branches = hide_merged_branches + settings.all = all if sys.platform == "linux" or sys.platform == "darwin": repo_name = git.repo.Repo( diff --git a/git_sim/branch.py b/git_sim/branch.py index 31f5f13..b61ae0f 100644 --- a/git_sim/branch.py +++ b/git_sim/branch.py @@ -17,8 +17,8 @@ def construct(self): self.show_intro() self.parse_commits() - self.recenter_frame() - self.scale_frame() + self.parse_all() + self.center_frame_on_commit(self.get_commit()) branchText = m.Text( self.name, @@ -47,6 +47,8 @@ def construct(self): self.toFadeOut.add(branchRec, branchText) self.drawnRefs[self.name] = fullbranch + self.recenter_frame() + self.scale_frame() self.fadeout() self.show_outro() diff --git a/git_sim/cherrypick.py b/git_sim/cherrypick.py index c079599..2092a95 100644 --- a/git_sim/cherrypick.py +++ b/git_sim/cherrypick.py @@ -57,6 +57,7 @@ def construct(self): self.parse_commits(head_commit) cherry_picked_commit = self.get_commit(self.commit) self.parse_commits(cherry_picked_commit, shift=4 * m.DOWN) + self.parse_all() self.center_frame_on_commit(head_commit) self.setup_and_draw_parent( head_commit, diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index abcb692..986ad53 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -19,18 +19,17 @@ def __init__(self): self.drawnCommits = {} self.drawnRefs = {} self.drawnCommitIds = {} - self.zoomOuts = 0 self.toFadeOut = m.Group() - self.trimmed = False self.prevRef = None self.topref = None self.n_default = settings.n_default self.n = settings.n self.n_orig = self.n self.selected_branches = [] - self.stop = False self.zone_title_offset = 2.6 if platform.system() == "Windows" else 2.6 self.arrow_map = [] + self.all = settings.all + self.first_parse = True self.logo = m.ImageMobject(settings.logo) self.logo.width = 3 @@ -97,6 +96,7 @@ def parse_commits( if i == 0 and len(self.drawnRefs) < 2: self.draw_dark_ref() + self.first_parse = False if i < self.n: i += 1 commitParents = list(commit.parents) @@ -110,6 +110,11 @@ def parse_commits( for p in range(len(commitParents)): self.parse_commits(commitParents[p], i, circle) + def parse_all(self): + if self.all: + for branch in self.get_nonparent_branch_names(): + self.parse_commits(self.get_commit(branch.name)) + def show_intro(self): if settings.animate and settings.show_intro: self.add(self.logo) @@ -195,7 +200,7 @@ def draw_commit(self, commit, i, prevCircle, shift=numpy.array([0.0, 0.0, 0.0])) ) while any((circle.get_center() == c).all() for c in self.get_centers()): - circle.next_to(circle, m.DOWN, buff=3.5) + circle.shift(m.DOWN * 4) isNewCommit = commit.hexsha not in self.drawnCommits @@ -324,7 +329,7 @@ def draw_head(self, commit, i, commitId): self.drawnRefs["HEAD"] = head self.prevRef = head - if i == 0: + if i == 0 and self.first_parse: self.topref = self.prevRef def draw_branch(self, commit, i): @@ -340,8 +345,6 @@ def draw_branch(self, commit, i): branches.insert(0, branches.pop(branches.index(selected_branch))) for branch in branches: - # Use forward slash to check if branch is local or remote tracking - # and draw the branch label if its hexsha matches the current commit if ( not self.is_remote_tracking_branch(branch) # local branch and commit.hexsha == self.repo.heads[branch].commit.hexsha @@ -375,7 +378,7 @@ def draw_branch(self, commit, i): self.toFadeOut.add(branchRec, branchText) self.drawnRefs[branch] = fullbranch - if i == 0: + if i == 0 and self.first_parse: self.topref = self.prevRef x += 1 @@ -421,7 +424,7 @@ def draw_tag(self, commit, i): self.toFadeOut.add(tagRec, tagText) - if i == 0: + if i == 0 and self.first_parse: self.topref = self.prevRef x += 1 @@ -925,7 +928,7 @@ def draw_ref(self, commit, i, top, text="HEAD", color=m.BLUE): self.drawnRefs[text] = ref self.prevRef = ref - if i == 0: + if i == 0 and self.first_parse: self.topref = self.prevRef def draw_dark_ref(self): diff --git a/git_sim/log.py b/git_sim/log.py index 8cb8cf4..4c0c277 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -20,22 +20,27 @@ def __init__(self, ctx: typer.Context, n: int, all: bool): self.n = n self.n_orig = self.n + all_command = ctx.parent.params.get("all") + self.all_subcommand = all + if self.all_subcommand: + all = self.all_subcommand + else: + all = all_command + self.all = all + try: self.selected_branches.append(self.repo.active_branch.name) except TypeError: pass - self.all = all def construct(self): if not settings.stdout: print( - f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all else ''}{' -n ' + str(self.n) if self.n_subcommand else ''}" + f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all_subcommand else ''}{' -n ' + str(self.n) if self.n_subcommand else ''}" ) self.show_intro() self.parse_commits() - if self.all: - for branch in self.get_nonparent_branch_names(): - self.parse_commits(self.get_commit(branch.name)) + self.parse_all() self.recenter_frame() self.scale_frame() self.fadeout() @@ -45,13 +50,13 @@ def construct(self): def log( ctx: typer.Context, n: int = typer.Option( - settings.n_subcommand, + None, "-n", help="Number of commits to display from branch heads", - min=1, ), all: bool = typer.Option( False, + "--all", help="Display all local branches in the log output", ), ): diff --git a/git_sim/merge.py b/git_sim/merge.py index 0aaf9aa..4fc1a63 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -70,6 +70,7 @@ def construct(self): if self.ff: self.parse_commits(branch_commit) + self.parse_all() reset_head_to = branch_commit.hexsha shift = numpy.array([0.0, 0.6, 0.0]) @@ -95,6 +96,7 @@ def construct(self): else: self.parse_commits(head_commit) self.parse_commits(branch_commit, shift=4 * m.DOWN) + self.parse_all() self.center_frame_on_commit(head_commit) self.setup_and_draw_parent( head_commit, diff --git a/git_sim/rebase.py b/git_sim/rebase.py index c8b40bc..f9438dc 100644 --- a/git_sim/rebase.py +++ b/git_sim/rebase.py @@ -76,6 +76,7 @@ def construct(self): reached_base = True self.parse_commits(head_commit, shift=4 * m.DOWN) + self.parse_all() self.center_frame_on_commit(branch_commit) to_rebase = [] diff --git a/git_sim/settings.py b/git_sim/settings.py index 705f75e..c12cae6 100644 --- a/git_sim/settings.py +++ b/git_sim/settings.py @@ -21,7 +21,6 @@ class Settings(BaseSettings): auto_open = True n_default = 5 n = 5 - n_subcommand: Union[int, None] = None files: Union[List[pathlib.Path], None] = None hide_first_tag = False img_format: ImgFormat = ImgFormat.jpg @@ -43,6 +42,7 @@ class Settings(BaseSettings): stdout = False invert_branches = False hide_merged_branches = False + all = False class Config: env_prefix = "git_sim_" diff --git a/git_sim/tag.py b/git_sim/tag.py index 58de08c..19924cd 100644 --- a/git_sim/tag.py +++ b/git_sim/tag.py @@ -17,8 +17,8 @@ def construct(self): self.show_intro() self.parse_commits() - self.recenter_frame() - self.scale_frame() + self.parse_all() + self.center_frame_on_commit(self.get_commit()) tagText = m.Text( self.name, @@ -45,7 +45,10 @@ def construct(self): self.add(fulltag) self.toFadeOut.add(tagRec, tagText) + self.drawnRefs[self.name] = fulltag + self.recenter_frame() + self.scale_frame() self.fadeout() self.show_outro() From 95c6fdfb834b1016a3344e361b1d15d1c79557c5 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 19:59:15 -0800 Subject: [PATCH 15/19] Fix bugs as a result of merge Signed-off-by: Jacob Stopak --- git_sim/__main__.py | 1 + git_sim/git_sim_base_command.py | 2 +- git_sim/log.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/git_sim/__main__.py b/git_sim/__main__.py index f33c9ef..570d17a 100644 --- a/git_sim/__main__.py +++ b/git_sim/__main__.py @@ -121,6 +121,7 @@ def main( "--quiet", "-q", help="Suppress all output except errors", + ), invert_branches: bool = typer.Option( settings.invert_branches, help="Invert positioning of branches by reversing order of multiple parents where applicable", diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index 986ad53..ee7c3f5 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -905,7 +905,7 @@ def get_nondark_commits(self): nondark_commits = [] return nondark_commits - def draw_ref(self, commit, i, top, text="HEAD", color=m.BLUE): + def draw_ref(self, commit, top, i=0, text="HEAD", color=m.BLUE): refText = m.Text(text, font="Monospace", font_size=20, color=self.fontColor) refbox = m.Rectangle( color=color, diff --git a/git_sim/log.py b/git_sim/log.py index a41e3fb..93c7e4e 100644 --- a/git_sim/log.py +++ b/git_sim/log.py @@ -34,7 +34,6 @@ def __init__(self, ctx: typer.Context, n: int, all: bool): pass def construct(self): -<<<<<<< HEAD if not settings.stdout and not settings.output_only_path and not settings.quiet: print( f"{settings.INFO_STRING} {type(self).__name__.lower()}{' --all' if self.all_subcommand else ''}{' -n ' + str(self.n) if self.n_subcommand else ''}" From 2cb2dcbb478131f1f1ac0251b9d661365c0ecabd Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 21:17:14 -0800 Subject: [PATCH 16/19] Fix positioning of refs after fast forward merge Signed-off-by: Jacob Stopak --- git_sim/git_sim_base_command.py | 14 ++++++++++++++ git_sim/merge.py | 5 +++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index ee7c3f5..a3e3b79 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -818,6 +818,20 @@ def reset_head_branch(self, hexsha, shift=numpy.array([0.0, 0.0, 0.0])): ) ) + def reset_head_branch_to_ref(self, ref, shift=numpy.array([0.0, 0.0, 0.0])): + if settings.animate: + self.play(self.drawnRefs["HEAD"].animate.next_to(ref, m.UP)) + self.play( + self.drawnRefs[self.repo.active_branch.name].animate.next_to( + self.drawnRefs["HEAD"], m.UP + ) + ) + else: + self.drawnRefs["HEAD"].next_to(ref, m.UP) + self.drawnRefs[self.repo.active_branch.name].next_to( + self.drawnRefs["HEAD"], m.UP + ) + def translate_frame(self, shift): if settings.animate: self.play(self.camera.frame.animate.shift(shift)) diff --git a/git_sim/merge.py b/git_sim/merge.py index 51813d3..ae73f42 100644 --- a/git_sim/merge.py +++ b/git_sim/merge.py @@ -58,7 +58,6 @@ def construct(self): head_commit = self.get_commit() branch_commit = self.get_commit(self.branch) - # Use forward slash to determine if supplied branch arg is local or remote tracking branch if not self.is_remote_tracking_branch(self.branch): if self.branch in self.repo.git.branch("--contains", head_commit.hexsha): self.ff = True @@ -82,8 +81,10 @@ def construct(self): self.recenter_frame() self.scale_frame() - if "HEAD" in self.drawnRefs: + if "HEAD" in self.drawnRefs and self.no_ff: self.reset_head_branch(reset_head_to, shift=shift) + elif "HEAD" in self.drawnRefs: + self.reset_head_branch_to_ref(self.topref, shift=shift) else: self.draw_ref(branch_commit, commitId if self.no_ff else self.topref) self.draw_ref( From 9b724cd343a24b02042eb6b3ffca46b776daac7f Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 22:02:26 -0800 Subject: [PATCH 17/19] Fix number of commits displayed using -n option with table/zone subcommands Signed-off-by: Jacob Stopak --- git_sim/add.py | 2 ++ git_sim/commit.py | 1 + git_sim/reset.py | 3 ++- git_sim/restore.py | 3 ++- git_sim/revert.py | 2 +- git_sim/stash.py | 3 ++- git_sim/status.py | 1 + 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/git_sim/add.py b/git_sim/add.py index 4758157..cd436e4 100644 --- a/git_sim/add.py +++ b/git_sim/add.py @@ -17,6 +17,7 @@ def __init__(self, files: List[str]): self.allow_no_commits = True self.files = files settings.hide_merged_branches = True + self.n = self.n_default try: self.selected_branches.append(self.repo.active_branch.name) @@ -89,5 +90,6 @@ def add( help="The names of one or more files to add to Git's staging area", ) ): + settings.hide_first_tag = True scene = Add(files=files) handle_animations(scene=scene) diff --git a/git_sim/commit.py b/git_sim/commit.py index c31ecc4..2152007 100644 --- a/git_sim/commit.py +++ b/git_sim/commit.py @@ -116,5 +116,6 @@ def commit( help="Amend the last commit message, must be used with the --message flag", ), ): + settings.hide_first_tag = True scene = Commit(message=message, amend=amend) handle_animations(scene=scene) diff --git a/git_sim/reset.py b/git_sim/reset.py index 2f3495d..3f12e08 100644 --- a/git_sim/reset.py +++ b/git_sim/reset.py @@ -35,7 +35,7 @@ def __init__( sys.exit(1) self.commitsSinceResetTo = list(self.repo.iter_commits(self.commit + "...HEAD")) - self.hide_first_tag = True + self.n = self.n_default try: self.selected_branches.append(self.repo.active_branch.name) @@ -169,5 +169,6 @@ def reset( help="Simulate a soft reset, shortcut for --mode=hard", ), ): + settings.hide_first_tag = True scene = Reset(commit=commit, mode=mode, soft=soft, mixed=mixed, hard=hard) handle_animations(scene=scene) diff --git a/git_sim/restore.py b/git_sim/restore.py index 9cd5354..378833b 100644 --- a/git_sim/restore.py +++ b/git_sim/restore.py @@ -12,9 +12,9 @@ class Restore(GitSimBaseCommand): def __init__(self, files: List[str]): super().__init__() - self.hide_first_tag = True self.files = files settings.hide_merged_branches = True + self.n = self.n_default try: self.selected_branches.append(self.repo.active_branch.name) @@ -79,5 +79,6 @@ def restore( help="The names of one or more files to restore", ) ): + settings.hide_first_tag = True scene = Restore(files=files) handle_animations(scene=scene) diff --git a/git_sim/revert.py b/git_sim/revert.py index 289c93f..32f0b55 100644 --- a/git_sim/revert.py +++ b/git_sim/revert.py @@ -29,7 +29,6 @@ def __init__(self, commit: str): self.n = self.n_default settings.hide_merged_branches = True - self.hide_first_tag = True self.zone_title_offset += 0.1 try: @@ -166,5 +165,6 @@ def revert( help="The ref (branch/tag), or commit ID to simulate revert", ) ): + settings.hide_first_tag = True scene = Revert(commit=commit) handle_animations(scene=scene) diff --git a/git_sim/stash.py b/git_sim/stash.py index c69c105..d490817 100644 --- a/git_sim/stash.py +++ b/git_sim/stash.py @@ -19,11 +19,11 @@ class StashSubCommand(Enum): class Stash(GitSimBaseCommand): def __init__(self, files: List[str], command: StashSubCommand): super().__init__() - self.hide_first_tag = True self.files = files self.no_files = True if not self.files else False self.command = command settings.hide_merged_branches = True + self.n = self.n_default try: self.selected_branches.append(self.repo.active_branch.name) @@ -198,5 +198,6 @@ def stash( help="The name of the file to stash changes for", ), ): + settings.hide_first_tag = True scene = Stash(files=files, command=command) handle_animations(scene=scene) diff --git a/git_sim/status.py b/git_sim/status.py index d88ec77..75fa360 100644 --- a/git_sim/status.py +++ b/git_sim/status.py @@ -11,6 +11,7 @@ def __init__(self): except TypeError: pass settings.hide_merged_branches = True + self.n = self.n_default def construct(self): if not settings.stdout and not settings.output_only_path and not settings.quiet: From 5a8bc89df4419fedcd8cc5957db8baffeadfa7b0 Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 22:09:17 -0800 Subject: [PATCH 18/19] Remove redundant if statement Signed-off-by: Jacob Stopak --- git_sim/git_sim_base_command.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/git_sim/git_sim_base_command.py b/git_sim/git_sim_base_command.py index a3e3b79..196ee82 100644 --- a/git_sim/git_sim_base_command.py +++ b/git_sim/git_sim_base_command.py @@ -97,18 +97,17 @@ def parse_commits( self.draw_dark_ref() self.first_parse = False - if i < self.n: - i += 1 - commitParents = list(commit.parents) - if len(commitParents) > 0: - if settings.invert_branches: - commitParents.reverse() - - if settings.hide_merged_branches: - self.parse_commits(commitParents[0], i, circle) - else: - for p in range(len(commitParents)): - self.parse_commits(commitParents[p], i, circle) + i += 1 + commitParents = list(commit.parents) + if len(commitParents) > 0: + if settings.invert_branches: + commitParents.reverse() + + if settings.hide_merged_branches: + self.parse_commits(commitParents[0], i, circle) + else: + for p in range(len(commitParents)): + self.parse_commits(commitParents[p], i, circle) def parse_all(self): if self.all: From a45db4cbf07e74cad131698e9959e8f728488c3e Mon Sep 17 00:00:00 2001 From: Jacob Stopak Date: Thu, 2 Mar 2023 22:09:26 -0800 Subject: [PATCH 19/19] Bump version to 0.2.6 Signed-off-by: Jacob Stopak --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ce18cf9..f9c5df1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="git-sim", - version="0.2.5", + version="0.2.6", author="Jacob Stopak", author_email="jacob@initialcommit.io", description="Simulate Git commands on your own repos by generating an image (default) or video visualization depicting the command's behavior.",