Skip to content

Commit 5735021

Browse files
authored
Fix branch updating and caching in git handler (#24)
- URLs ending with `.git` should now produce the same hash as those not ending with `.git`. - Hashes are consistent across runs. - Updated logging to include the exact git error message. - Added a function that checks for potential changes and logs them before reverting - this should prevent errors during branch updates and provide more insight into what’s happening. Fixes #23
1 parent 37be50c commit 5735021

1 file changed

Lines changed: 57 additions & 8 deletions

File tree

src/tmt_web/utils/git_handler.py

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
It uses tmt's Git utilities for robust clone operations with retry logic.
77
"""
88

9+
import hashlib
910
import re
1011
from shutil import rmtree
1112

@@ -20,15 +21,22 @@
2021
ROOT_DIR = Path(__file__).resolve().parents[2]
2122

2223

24+
def create_hash(text: str):
25+
"""Create hash of the given text that is consistent across runs."""
26+
hashed_text = hashlib.new("sha1", usedforsecurity=False)
27+
hashed_text.update(text.encode())
28+
return hashed_text.hexdigest()
29+
30+
2331
def get_unique_clone_path(url: str) -> Path:
2432
"""
2533
Generate a unique path for cloning a repository.
2634
2735
:param url: Repository URL
2836
:return: Unique path for cloning
2937
"""
30-
url = url.rstrip("/")
31-
clone_dir_name = str(abs(hash(url)))
38+
url = url.rstrip("/").removesuffix(".git")
39+
clone_dir_name = create_hash(url)
3240
return ROOT_DIR / settings.CLONE_DIR_PATH / clone_dir_name
3341

3442

@@ -102,9 +110,11 @@ def get_git_repository(url: str, logger: Logger, ref: str | None = None) -> Path
102110
try:
103111
common.run(Command("git", "checkout", ref), cwd=destination)
104112
except RunError as err:
105-
logger.fail(f"Failed to checkout ref '{ref}'")
113+
logger.fail(f"Failed to checkout ref '{ref}': {err.stderr}")
106114
raise AttributeError(f"Failed to checkout ref '{ref}'") from err
107115

116+
_ensure_no_changes(common, destination, logger)
117+
108118
# If the ref is a branch, ensure it's up to date
109119
if _is_branch(common, destination, ref):
110120
_update_branch(common, destination, ref, logger)
@@ -127,7 +137,9 @@ def _get_default_branch(common: Common, repo_path: Path, logger: Logger) -> str:
127137
raise GeneralError(f"Failed to determine default branch for repository '{repo_path}'")
128138

129139
except RunError as err:
130-
logger.fail(f"Failed to determine default branch for repository '{repo_path}'")
140+
logger.fail(
141+
f"Failed to determine default branch for repository '{repo_path}': {err.stderr}"
142+
)
131143
raise GeneralError(
132144
f"Failed to determine default branch for repository '{repo_path}'"
133145
) from err
@@ -136,9 +148,21 @@ def _get_default_branch(common: Common, repo_path: Path, logger: Logger) -> str:
136148
def _fetch_remote(common: Common, repo_path: Path, logger: Logger) -> None:
137149
"""Fetch updates from the remote repository."""
138150
try:
139-
common.run(Command("git", "fetch"), cwd=repo_path)
151+
# Fetch all branches and tags, prune deleted ones
152+
common.run(
153+
Command(
154+
"git",
155+
"fetch",
156+
"origin",
157+
"--prune",
158+
"--prune-tags",
159+
"+refs/heads/*:refs/remotes/origin/*",
160+
"+refs/tags/*:refs/tags/*",
161+
),
162+
cwd=repo_path,
163+
)
140164
except RunError as err:
141-
logger.fail(f"Failed to fetch remote for repository '{repo_path}'")
165+
logger.fail(f"Failed to fetch remote for repository '{repo_path}': {err.stderr}")
142166
raise GeneralError(f"Failed to fetch remote for repository '{repo_path}'") from err
143167

144168

@@ -147,7 +171,7 @@ def _update_branch(common: Common, repo_path: Path, branch: str, logger: Logger)
147171
try:
148172
common.run(Command("git", "show-branch", f"origin/{branch}"), cwd=repo_path)
149173
except RunError as err:
150-
logger.fail(f"Branch '{branch}' does not exist in repository '{repo_path}'")
174+
logger.fail(f"Branch '{branch}' does not exist in repository '{repo_path}': {err.stderr}")
151175
raise GeneralError(f"Branch {branch}' does not exist in repository '{repo_path}'") from err
152176
try:
153177
# Check if the branch is already up to date
@@ -158,12 +182,37 @@ def _update_branch(common: Common, repo_path: Path, branch: str, logger: Logger)
158182
try:
159183
common.run(Command("git", "reset", "--hard", f"origin/{branch}"), cwd=repo_path)
160184
except RunError as err:
161-
logger.fail(f"Failed to update branch '{branch}' for repository '{repo_path}'")
185+
logger.fail(
186+
f"Failed to update branch '{branch}' for repository '{repo_path}': {err.stderr}"
187+
)
162188
raise GeneralError(
163189
f"Failed to update branch '{branch}' for repository '{repo_path}'"
164190
) from err
165191

166192

193+
def _ensure_no_changes(common: Common, repo_path: Path, logger: Logger) -> None:
194+
"""Ensure there are no changes in the repository."""
195+
try:
196+
output = common.run(Command("git", "status", "--porcelain"), cwd=repo_path)
197+
if not output.stdout or not output.stdout.strip():
198+
return
199+
logger.warning(f"Repository '{repo_path}' has changes:\n{output.stdout.strip()}")
200+
except RunError as err:
201+
logger.fail(f"Failed to check repository status for '{repo_path}': {err.stderr}")
202+
raise GeneralError(f"Failed to check repository status for '{repo_path}'") from err
203+
204+
try:
205+
common.run(Command("git", "restore", "."), cwd=repo_path)
206+
common.run(Command("git", "clean", "-fdx"), cwd=repo_path)
207+
except RunError as err:
208+
logger.fail(
209+
f"Repository '{repo_path}' has changes that could not be reverted: {err.stderr}"
210+
)
211+
raise GeneralError(
212+
f"Repository '{repo_path}' has changes that could not be reverted"
213+
) from err
214+
215+
167216
def _is_branch(common: Common, repo_path: Path, ref: str) -> bool:
168217
"""
169218
Check if the given ref is a branch in the Git repository.

0 commit comments

Comments
 (0)