Skip to content

Commit

Permalink
Improve lazy loading of git
Browse files Browse the repository at this point in the history
  • Loading branch information
astrojuanlu committed Aug 6, 2023
1 parent 4467792 commit 064c2be
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 28 deletions.
4 changes: 3 additions & 1 deletion copier/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
StrSeq,
)
from .user_data import DEFAULT_DATA, AnswersMap, Question
from .vcs import git
from .vcs import get_git


@dataclass(config=ConfigDict(extra="forbid"))
Expand Down Expand Up @@ -813,6 +813,7 @@ def run_update(self) -> None:
self._apply_update()

def _apply_update(self):
git = get_git()
subproject_top = Path(
git(
"-C",
Expand Down Expand Up @@ -929,6 +930,7 @@ def _apply_update(self):

def _git_initialize_repo(self):
"""Initialize a git repository in the current directory."""
git = get_git()
git("init", retcode=None)
git("add", ".")
git("config", "user.name", "Copier")
Expand Down
4 changes: 2 additions & 2 deletions copier/subproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from .template import Template
from .types import AbsolutePath, AnyByStrDict, VCSTypes
from .vcs import git, is_in_git_repo
from .vcs import get_git, is_in_git_repo


@dataclass
Expand All @@ -41,7 +41,7 @@ def is_dirty(self) -> bool:
"""
if self.vcs == "git":
with local.cwd(self.local_abspath):
return bool(git("status", "--porcelain").strip())
return bool(get_git()("status", "--porcelain").strip())
return False

def _cleanup(self):
Expand Down
6 changes: 3 additions & 3 deletions copier/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
)
from .tools import copier_version, handle_remove_readonly
from .types import AnyByStrDict, Env, OptStr, StrSeq, Union, VCSTypes
from .vcs import checkout_latest_tag, clone, get_repo, git
from .vcs import checkout_latest_tag, clone, get_repo, get_git

# Default list of files in the template to exclude from the rendered project
DEFAULT_EXCLUDE: Tuple[str, ...] = (
Expand Down Expand Up @@ -248,13 +248,13 @@ def commit(self) -> OptStr:
"""If the template is VCS-tracked, get its commit description."""
if self.vcs == "git":
with local.cwd(self.local_abspath):
return git("describe", "--tags", "--always").strip()
return get_git()("describe", "--tags", "--always").strip()

@cached_property
def commit_hash(self) -> OptStr:
"""If the template is VCS-tracked, get its commit full hash."""
if self.vcs == "git":
return git("-C", self.local_abspath, "rev-parse", "HEAD").strip()
return get_git()("-C", self.local_abspath, "rev-parse", "HEAD").strip()

@cached_property
def config_data(self) -> AnyByStrDict:
Expand Down
48 changes: 26 additions & 22 deletions copier/vcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@
from .errors import DirtyLocalWarning, ShallowCloneWarning
from .types import OptBool, OptStr, StrOrPath

try:
from plumbum.cmd import git

GIT_VERSION = Version(re.findall(r"\d+\.\d+\.\d+", git("version"))[0])
except ImportError:
def get_git():
"""Gets `git` command, or fails if it's not available"""
try:
from plumbum.cmd import git
return git
except ImportError as exc:
raise OSError("git is not available") from exc

try:
GIT_VERSION = Version(re.findall(r"\d+\.\d+\.\d+", get_git()("version"))[0])
except OSError:
import warnings

warnings.warn(
"git command not found, some functionality might not work", UserWarning
)

def git(*args, **kwargs):
raise OSError("git is not available")

GIT_VERSION = None
GIT_VERSION = None # type: ignore


GIT_PREFIX = ("git@", "git://", "git+", "https://github.com/", "https://gitlab.com/")
Expand All @@ -45,15 +49,15 @@ def is_git_repo_root(path: StrOrPath) -> bool:
"""Indicate if a given path is a git repo root directory."""
try:
with local.cwd(Path(path, ".git")):
return git("rev-parse", "--is-inside-git-dir").strip() == "true"
return get_git()("rev-parse", "--is-inside-git-dir").strip() == "true"
except OSError:
return False


def is_in_git_repo(path: StrOrPath) -> bool:
"""Indicate if a given path is in a git repo directory."""
try:
git("-C", path, "rev-parse", "--show-toplevel")
get_git()("-C", path, "rev-parse", "--show-toplevel")
return True
except (OSError, ProcessExecutionError):
return False
Expand All @@ -62,7 +66,7 @@ def is_in_git_repo(path: StrOrPath) -> bool:
def is_git_shallow_repo(path: StrOrPath) -> bool:
"""Indicate if a given path is a git shallow repo directory."""
try:
return git("-C", path, "rev-parse", "--is-shallow-repository").strip() == "true"
return get_git()("-C", path, "rev-parse", "--is-shallow-repository").strip() == "true"
except (OSError, ProcessExecutionError):
return False

Expand All @@ -74,8 +78,8 @@ def is_git_bundle(path: Path) -> bool:
try:
with TemporaryDirectory(prefix=f"{__name__}.is_git_bundle.") as dirname:
with local.cwd(dirname):
git("init")
return bool(git["bundle", "verify", path] & TF)
get_git()("init")
return bool(get_git()["bundle", "verify", path] & TF)
except (OSError, ProcessExecutionError):
return False

Expand Down Expand Up @@ -126,7 +130,7 @@ def checkout_latest_tag(local_repo: StrOrPath, use_prereleases: OptBool = False)
If `False`, skip prerelease git tags.
"""
with local.cwd(local_repo):
all_tags = filter(valid_version, git("tag").split())
all_tags = filter(valid_version, get_git()("tag").split())
if not use_prereleases:
all_tags = filter(
lambda tag: not version.parse(tag).is_prerelease, all_tags
Expand All @@ -140,8 +144,8 @@ def checkout_latest_tag(local_repo: StrOrPath, use_prereleases: OptBool = False)
file=sys.stderr,
)
latest_tag = "HEAD"
git("checkout", "--force", latest_tag)
git("submodule", "update", "--checkout", "--init", "--recursive", "--force")
get_git()("checkout", "--force", latest_tag)
get_git()("submodule", "update", "--checkout", "--init", "--recursive", "--force")
return latest_tag


Expand All @@ -159,7 +163,7 @@ def clone(url: str, ref: OptStr = None) -> str:
Reference to checkout. For Git repos, defaults to `HEAD`.
"""
location = mkdtemp(prefix=f"{__name__}.clone.")
_clone = git["clone", "--no-checkout", url, location]
_clone = get_git()["clone", "--no-checkout", url, location]
# Faster clones if possible
if GIT_VERSION >= Version("2.27"):
url_match = re.match("(file://)?(.*)", url)
Expand All @@ -180,12 +184,12 @@ def clone(url: str, ref: OptStr = None) -> str:
if ref in {None, "HEAD"} and os.path.exists(url) and Path(url).is_dir():
is_dirty = False
with local.cwd(url):
is_dirty = bool(git("status", "--porcelain").strip())
is_dirty = bool(get_git()("status", "--porcelain").strip())
if is_dirty:
url_abspath = Path(url).absolute()
with local.cwd(location):
git("--git-dir=.git", f"--work-tree={url_abspath}", "add", "-A")
git(
get_git()("--git-dir=.git", f"--work-tree={url_abspath}", "add", "-A")
get_git()(
"--git-dir=.git",
f"--work-tree={url_abspath}",
"commit",
Expand All @@ -199,8 +203,8 @@ def clone(url: str, ref: OptStr = None) -> str:
)

with local.cwd(location):
git("checkout", "-f", ref or "HEAD")
git("submodule", "update", "--checkout", "--init", "--recursive", "--force")
get_git()("checkout", "-f", ref or "HEAD")
get_git()("submodule", "update", "--checkout", "--init", "--recursive", "--force")

return location

Expand Down

0 comments on commit 064c2be

Please sign in to comment.