Skip to content

Commit 8b3405f

Browse files
Add clone subcommand
Signed-off-by: Jacob Stopak <jacob@initialcommit.io>
1 parent 226887b commit 8b3405f

File tree

3 files changed

+132
-10
lines changed

3 files changed

+132
-10
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Example: `$ git-sim merge <branch>`
2626

2727
## Features
2828
- Run a one-liner git-sim command in the terminal to generate a custom Git command visualization (.jpg) from your repo
29-
- Supported commands: `log`, `status`, `add`, `restore`, `commit`, `stash`, `branch`, `tag`, `reset`, `revert`, `merge`, `rebase`, `cherry-pick`, `switch`, `checkout`, `fetch`, `pull`, `push`
29+
- Supported commands: `log`, `status`, `add`, `restore`, `commit`, `stash`, `branch`, `tag`, `reset`, `revert`, `merge`, `rebase`, `cherry-pick`, `switch`, `checkout`, `fetch`, `pull`, `push`, `clone`
3030
- Generate an animated video (.mp4) instead of a static image using the `--animate` flag (note: significant performance slowdown, it is recommended to use `--low-quality` to speed up testing and remove when ready to generate presentation-quality video)
3131
- Color commits by parameter, such as author the `--color-by=author` option
3232
- Choose between dark mode (default) and light mode
@@ -126,7 +126,7 @@ $ git-sim <subcommand> -h
126126
* [Manim (Community version)](https://www.manim.community/)
127127

128128
## Commands
129-
Basic usage is similar to Git itself - `git-sim` takes a familiar set of subcommands including "log", "status", "add", "restore", "commit", "stash", "branch", "tag", "reset", "revert", "merge", "rebase", "cherry-pick", "switch", "checkout", "fetch", "pull", "push" along with corresponding options.
129+
Basic usage is similar to Git itself - `git-sim` takes a familiar set of subcommands including "log", "status", "add", "restore", "commit", "stash", "branch", "tag", "reset", "revert", "merge", "rebase", "cherry-pick", "switch", "checkout", "fetch", "pull", "push", "clone", along with corresponding options.
130130

131131
```console
132132
$ git-sim [global options] <subcommand> [subcommand options]
@@ -311,6 +311,12 @@ Usage: `git-sim push [<remote> <branch>]`
311311
- If `<remote>` and `<branch>` are not specified, the active branch is pushed to the default remote
312312
- If the push fails due to remote changes that don't exist in the local repo, a message is included telling the user to pull first, along with color coding which commits need to be pulled
313313

314+
### git clone
315+
Usage: `git-sim clone <url>`
316+
317+
- Clone the remote repo from `<url>` (web URL or filesystem path) to a new folder in the current directory
318+
- Output will report if clone operation is successful and show log of local clone
319+
314320
## Video animation examples
315321
```console
316322
$ git-sim --animate reset HEAD^

git_sim/__main__.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import git_sim.fetch
2525
import git_sim.pull
2626
import git_sim.push
27+
import git_sim.clone
2728

2829
from git_sim.settings import ImgFormat, VideoFormat, settings
2930
from manim import config, WHITE
@@ -171,14 +172,17 @@ def main(
171172
settings.all = all
172173
settings.color_by = color_by
173174

174-
if sys.platform == "linux" or sys.platform == "darwin":
175-
repo_name = git.repo.Repo(
176-
search_parent_directories=True
177-
).working_tree_dir.split("/")[-1]
178-
elif sys.platform == "win32":
179-
repo_name = git.repo.Repo(
180-
search_parent_directories=True
181-
).working_tree_dir.split("\\")[-1]
175+
try:
176+
if sys.platform == "linux" or sys.platform == "darwin":
177+
repo_name = git.repo.Repo(
178+
search_parent_directories=True
179+
).working_tree_dir.split("/")[-1]
180+
elif sys.platform == "win32":
181+
repo_name = git.repo.Repo(
182+
search_parent_directories=True
183+
).working_tree_dir.split("\\")[-1]
184+
except git.InvalidGitRepositoryError as e:
185+
repo_name = ""
182186

183187
settings.media_dir = os.path.join(settings.media_dir, repo_name)
184188

@@ -213,6 +217,7 @@ def main(
213217
app.command()(git_sim.fetch.fetch)
214218
app.command()(git_sim.pull.pull)
215219
app.command()(git_sim.push.push)
220+
app.command()(git_sim.clone.clone)
216221

217222

218223
if __name__ == "__main__":

git_sim/clone.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import sys
2+
import os
3+
from argparse import Namespace
4+
5+
import git
6+
import manim as m
7+
import numpy
8+
import typer
9+
import tempfile
10+
import shutil
11+
import stat
12+
import re
13+
14+
from git_sim.animations import handle_animations
15+
from git_sim.git_sim_base_command import GitSimBaseCommand
16+
from git_sim.settings import settings
17+
18+
19+
class Clone(GitSimBaseCommand):
20+
# Override since 'clone' subcommand shouldn't require repo to exist
21+
def init_repo(self):
22+
pass
23+
24+
def __init__(self, url: str):
25+
super().__init__()
26+
self.url = url
27+
settings.max_branches_per_commit = 2
28+
29+
def construct(self):
30+
if not settings.stdout and not settings.output_only_path and not settings.quiet:
31+
print(
32+
f"{settings.INFO_STRING } {type(self).__name__.lower()} {self.url}"
33+
)
34+
35+
self.show_intro()
36+
37+
# Configure paths to make local clone to run networked commands in
38+
repo_name = re.search(r"/([^/]+)/?$", self.url)
39+
if repo_name:
40+
repo_name = repo_name.group(1)
41+
if repo_name.endswith(".git"):
42+
repo_name = repo_name[:-4]
43+
else:
44+
print(f"git-sim error: Invalid repo URL, please confirm repo URL and try again")
45+
sys.exit(1)
46+
new_dir = os.path.join(tempfile.gettempdir(), "git_sim", repo_name)
47+
48+
# Create local clone of local repo
49+
try:
50+
self.repo = git.Repo.clone_from(self.url, new_dir, no_hardlinks=True)
51+
except git.GitCommandError as e:
52+
print(f"git-sim error: Invalid repo URL, please confirm repo URL and try again")
53+
sys.exit(1)
54+
55+
head_commit = self.get_commit()
56+
self.parse_commits(head_commit)
57+
self.recenter_frame()
58+
self.scale_frame()
59+
self.add_details(repo_name)
60+
self.color_by()
61+
self.fadeout()
62+
self.show_outro()
63+
64+
# Unlink the program from the filesystem
65+
self.repo.git.clear_cache()
66+
67+
# Delete the local clones
68+
shutil.rmtree(new_dir, onerror=del_rw)
69+
70+
def add_details(self, repo_name):
71+
text1 = m.Text(
72+
f"Successfully cloned from {self.url} into ./{repo_name}",
73+
font="Monospace",
74+
font_size=20,
75+
color=self.fontColor,
76+
weight=m.BOLD,
77+
)
78+
text1.move_to([self.camera.frame.get_center()[0], 4, 0])
79+
80+
text2 = m.Text(
81+
f"Cloned repo log:",
82+
font="Monospace",
83+
font_size=20,
84+
color=self.fontColor,
85+
weight=m.BOLD,
86+
)
87+
text2.move_to(text1.get_center()).shift(m.DOWN / 2)
88+
89+
self.toFadeOut.add(text1)
90+
self.toFadeOut.add(text2)
91+
self.recenter_frame()
92+
self.scale_frame()
93+
94+
if settings.animate:
95+
self.play(m.AddTextLetterByLetter(text1), m.AddTextLetterByLetter(text2))
96+
else:
97+
self.add(text1, text2)
98+
99+
100+
def del_rw(action, name, exc):
101+
os.chmod(name, stat.S_IWRITE)
102+
os.remove(name)
103+
104+
def clone(
105+
url: str = typer.Argument(
106+
...,
107+
help="The web URL or filesystem path of the Git repo to clone",
108+
),
109+
):
110+
scene = Clone(url=url)
111+
handle_animations(scene=scene)

0 commit comments

Comments
 (0)