|
2 | 2 | from __future__ import annotations
|
3 | 3 |
|
4 | 4 |
|
| 5 | +import contextlib |
5 | 6 | import datetime
|
6 | 7 | from pathlib import Path
|
| 8 | +import shutil |
7 | 9 | import subprocess
|
| 10 | +import re |
8 | 11 |
|
9 | 12 |
|
10 | 13 | import rich
|
@@ -128,3 +131,40 @@ def get_commits_between(dirname: PathLike, ref1: str, ref2: str) -> list[str]:
|
128 | 131 | def bisect_commits(dirname: PathLike, ref1: str, ref2: str) -> str:
|
129 | 132 | commits = get_commits_between(dirname, ref1, ref2)
|
130 | 133 | return commits[len(commits) // 2]
|
| 134 | + |
| 135 | + |
| 136 | +def clone( |
| 137 | + dirname: PathLike, |
| 138 | + url: str, |
| 139 | + *, |
| 140 | + branch: str | None = None, |
| 141 | + depth: int = 1, |
| 142 | +) -> None: |
| 143 | + is_hash = re.match(r"^[0-9a-f]{40}$", branch) if branch else False |
| 144 | + |
| 145 | + dirname = Path(dirname) |
| 146 | + if dirname.is_dir(): |
| 147 | + if is_hash and (dirname / ".git").is_dir() and get_git_hash(dirname) == branch: |
| 148 | + # This is a git repo, and the hash matches |
| 149 | + return |
| 150 | + shutil.rmtree(dirname) |
| 151 | + |
| 152 | + # Fetching a hash and fetching a branch require different approaches |
| 153 | + |
| 154 | + if is_hash: |
| 155 | + assert branch is not None |
| 156 | + dirname.mkdir() |
| 157 | + with contextlib.chdir(dirname): |
| 158 | + subprocess.check_call(["git", "init"]) |
| 159 | + subprocess.check_call(["git", "remote", "add", "origin", url]) |
| 160 | + subprocess.check_call( |
| 161 | + ["git", "fetch", "--depth", str(depth), "origin", branch] |
| 162 | + ) |
| 163 | + subprocess.check_call(["git", "checkout", branch]) |
| 164 | + else: |
| 165 | + args = ["git", "clone", url, str(dirname)] |
| 166 | + if branch is not None: |
| 167 | + args += ["--branch", branch] |
| 168 | + if depth is not None: |
| 169 | + args += ["--depth", str(depth)] |
| 170 | + subprocess.check_call(args) |
0 commit comments