edq.util.git
Handle interfacing with git repos.
1""" 2Handle interfacing with git repos. 3""" 4 5import os 6import re 7import typing 8 9import git 10 11UNKNOWN_VERSION: str = 'UNKNOWN' 12VERSION_LEN: int = 8 13DIRTY_SIFFIX: str = '-d' 14 15def get_version(path: str = '.', throw: bool = False) -> str: 16 """ 17 Get a version string from the git repo. 18 This is just a commit hash with some dressup. 19 """ 20 21 if (os.path.isfile(path)): 22 path = os.path.dirname(path) 23 24 try: 25 repo = git.Repo(path, search_parent_directories = True) 26 version = repo.head.commit.tree.hexsha[:VERSION_LEN] 27 except Exception as ex: 28 if (throw): 29 raise ValueError(f"Path '{path}' is not a valid Git repo.") from ex 30 31 return UNKNOWN_VERSION 32 33 if (repo.is_dirty()): 34 version += DIRTY_SIFFIX 35 36 return version 37 38def ensure_repo( 39 url: str, 40 path: str, 41 username: typing.Union[str, None] = None, 42 token: typing.Union[str, None] = None, 43 update: bool = False, 44 ref: typing.Union[str, None] = None, 45 ) -> None: 46 """ 47 Ensure that a git repo exists locally. 48 Clone the repo if it does not exist. 49 Optionally update (pull) the repo. 50 """ 51 52 if (os.path.isfile(path)): 53 raise ValueError(f"Target git path exists and is a file: '{path}'.") 54 55 if (not os.path.exists(path)): 56 clone(url, path, username = username, token = token) 57 58 repo = get_repo(path) 59 60 if (update): 61 update_repo(repo) 62 63 if (ref is not None): 64 checkout_repo(repo, ref) 65 66def get_repo(path: str) -> git.Repo: 67 """ Get a reference to a git repo. """ 68 69 return git.Repo(path) 70 71def clone( 72 url: str, 73 path: str, 74 username: typing.Union[str, None] = None, 75 token: typing.Union[str, None] = None, 76 ) -> git.Repo: 77 """ Clone a git repo to the target location and return a reference to it. """ 78 79 # If we have a username or password, we need to rewrite the URL. 80 # This is not very robust, but should work. 81 # After clone, the credentials are saved in the local git config. 82 if (username is not None): 83 if (token is None): 84 raise ValueError("If username is specified, a token must also be specified.") 85 86 auth_text = f"{username}:{token}" 87 url = re.sub(r'(http(s?)://)', rf"\1{auth_text}@", url) 88 89 return git.Repo.clone_from(url, path) 90 91def checkout_repo(repo: git.Repo, ref: str) -> None: 92 """ Checkout the given reference on the given repo. """ 93 94 repo.git.checkout(ref) 95 96def update_repo(repo: git.Repo) -> bool: 97 """ 98 Update (pull) the given repo. 99 Return true if an update occurred. 100 """ 101 102 fetch_results = repo.remotes.origin.pull() 103 104 for fetch_result in fetch_results: 105 if (fetch_result.ref.name != f"origin/{repo.active_branch.name}"): 106 continue 107 108 if (fetch_result.flags == git.remote.FetchInfo.HEAD_UPTODATE): 109 return False 110 111 return True 112 113 return False
UNKNOWN_VERSION: str =
'UNKNOWN'
VERSION_LEN: int =
8
DIRTY_SIFFIX: str =
'-d'
def
get_version(path: str = '.', throw: bool = False) -> str:
16def get_version(path: str = '.', throw: bool = False) -> str: 17 """ 18 Get a version string from the git repo. 19 This is just a commit hash with some dressup. 20 """ 21 22 if (os.path.isfile(path)): 23 path = os.path.dirname(path) 24 25 try: 26 repo = git.Repo(path, search_parent_directories = True) 27 version = repo.head.commit.tree.hexsha[:VERSION_LEN] 28 except Exception as ex: 29 if (throw): 30 raise ValueError(f"Path '{path}' is not a valid Git repo.") from ex 31 32 return UNKNOWN_VERSION 33 34 if (repo.is_dirty()): 35 version += DIRTY_SIFFIX 36 37 return version
Get a version string from the git repo. This is just a commit hash with some dressup.
def
ensure_repo( url: str, path: str, username: Optional[str] = None, token: Optional[str] = None, update: bool = False, ref: Optional[str] = None) -> None:
39def ensure_repo( 40 url: str, 41 path: str, 42 username: typing.Union[str, None] = None, 43 token: typing.Union[str, None] = None, 44 update: bool = False, 45 ref: typing.Union[str, None] = None, 46 ) -> None: 47 """ 48 Ensure that a git repo exists locally. 49 Clone the repo if it does not exist. 50 Optionally update (pull) the repo. 51 """ 52 53 if (os.path.isfile(path)): 54 raise ValueError(f"Target git path exists and is a file: '{path}'.") 55 56 if (not os.path.exists(path)): 57 clone(url, path, username = username, token = token) 58 59 repo = get_repo(path) 60 61 if (update): 62 update_repo(repo) 63 64 if (ref is not None): 65 checkout_repo(repo, ref)
Ensure that a git repo exists locally. Clone the repo if it does not exist. Optionally update (pull) the repo.
def
get_repo(path: str) -> git.repo.base.Repo:
67def get_repo(path: str) -> git.Repo: 68 """ Get a reference to a git repo. """ 69 70 return git.Repo(path)
Get a reference to a git repo.
def
clone( url: str, path: str, username: Optional[str] = None, token: Optional[str] = None) -> git.repo.base.Repo:
72def clone( 73 url: str, 74 path: str, 75 username: typing.Union[str, None] = None, 76 token: typing.Union[str, None] = None, 77 ) -> git.Repo: 78 """ Clone a git repo to the target location and return a reference to it. """ 79 80 # If we have a username or password, we need to rewrite the URL. 81 # This is not very robust, but should work. 82 # After clone, the credentials are saved in the local git config. 83 if (username is not None): 84 if (token is None): 85 raise ValueError("If username is specified, a token must also be specified.") 86 87 auth_text = f"{username}:{token}" 88 url = re.sub(r'(http(s?)://)', rf"\1{auth_text}@", url) 89 90 return git.Repo.clone_from(url, path)
Clone a git repo to the target location and return a reference to it.
def
checkout_repo(repo: git.repo.base.Repo, ref: str) -> None:
92def checkout_repo(repo: git.Repo, ref: str) -> None: 93 """ Checkout the given reference on the given repo. """ 94 95 repo.git.checkout(ref)
Checkout the given reference on the given repo.
def
update_repo(repo: git.repo.base.Repo) -> bool:
97def update_repo(repo: git.Repo) -> bool: 98 """ 99 Update (pull) the given repo. 100 Return true if an update occurred. 101 """ 102 103 fetch_results = repo.remotes.origin.pull() 104 105 for fetch_result in fetch_results: 106 if (fetch_result.ref.name != f"origin/{repo.active_branch.name}"): 107 continue 108 109 if (fetch_result.flags == git.remote.FetchInfo.HEAD_UPTODATE): 110 return False 111 112 return True 113 114 return False
Update (pull) the given repo. Return true if an update occurred.