-
Notifications
You must be signed in to change notification settings - Fork 388
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[cli] Handle use case when git is not available
It is possible that `git` is not available on the user's system where the `CodeChecker store` command is executed. In this case importing git module will fail. With this patch we will handle this use case and print a warning message to the user. This patch will also print some information to the user whether collecting blame information was successfull or if no blame information is available for source files.
- Loading branch information
1 parent
0eee1df
commit f9fc688
Showing
2 changed files
with
128 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import json | ||
import os | ||
import sys | ||
import zipfile | ||
|
||
from concurrent.futures import ProcessPoolExecutor | ||
from git import Repo | ||
from git.exc import InvalidGitRepositoryError | ||
from typing import Dict, List, Optional | ||
|
||
from codechecker_common.logger import get_logger | ||
|
||
LOG = get_logger('system') | ||
|
||
|
||
FileBlameInfo = Dict[str, Optional[Dict]] | ||
|
||
|
||
def __get_blame_info(file_path: str): | ||
""" Get blame info for the given file. """ | ||
try: | ||
repo = Repo(file_path, search_parent_directories=True) | ||
except InvalidGitRepositoryError: | ||
return | ||
|
||
tracking_branch = None | ||
try: | ||
# If a commit is checked out, accessing the active_branch member will | ||
# throw a type error. In this case we will use the current commit hash. | ||
tracking_branch = str(repo.active_branch.tracking_branch()) | ||
except TypeError: | ||
tracking_branch = repo.head.commit.hexsha | ||
|
||
try: | ||
blame = repo.blame_incremental(repo.head.commit.hexsha, file_path) | ||
|
||
res = { | ||
'version': 'v1', | ||
'tracking_branch': tracking_branch, | ||
'remote_url': next(repo.remote().urls, None), | ||
'commits': {}, | ||
'blame': []} | ||
|
||
for b in blame: | ||
commit = b.commit | ||
|
||
if commit.hexsha not in res['commits']: | ||
res['commits'][commit.hexsha] = { | ||
'author': { | ||
'name': commit.author.name, | ||
'email': commit.author.email, | ||
}, | ||
'summary': commit.summary, | ||
'message': commit.message, | ||
'committed_datetime': str(commit.committed_datetime)} | ||
|
||
res['blame'].append({ | ||
'from': b.linenos[0], | ||
'to': b.linenos[-1], | ||
'commit': commit.hexsha}) | ||
|
||
LOG.debug("Collected blame info for %s", file_path) | ||
|
||
return res | ||
except Exception as ex: | ||
LOG.debug("Failed to get blame information for %s: %s", file_path, ex) | ||
|
||
|
||
def __collect_blame_info_for_files( | ||
file_paths: List[str], | ||
zip_iter=map | ||
) -> FileBlameInfo: | ||
""" Collect blame information for the given file paths. """ | ||
file_blame_info = {} | ||
for file_path, blame_info in zip(file_paths, | ||
zip_iter(__get_blame_info, file_paths)): | ||
file_blame_info[file_path] = blame_info | ||
|
||
return file_blame_info | ||
|
||
|
||
def assemble_blame_info( | ||
zip_file: zipfile.ZipFile, | ||
file_paths: List[str] | ||
) -> bool: | ||
""" | ||
Collect and write blame information for the given files to the zip file. | ||
Returns true if at least one blame information is collected. | ||
""" | ||
# Currently ProcessPoolExecutor fails completely in windows. | ||
# Reason is most likely combination of venv and fork() not | ||
# being present in windows, so stuff like setting up | ||
# PYTHONPATH in parent CodeChecker before store is executed | ||
# are lost. | ||
if sys.platform == "win32": | ||
file_blame_info = __collect_blame_info_for_files(file_paths) | ||
else: | ||
with ProcessPoolExecutor() as executor: | ||
file_blame_info = __collect_blame_info_for_files( | ||
file_paths, executor.map) | ||
|
||
# Add blame information to the zip for the files which will be sent | ||
# to the server if exist. | ||
for f, blame_info in file_blame_info.items(): | ||
if blame_info: | ||
zip_file.writestr( | ||
os.path.join('blame', f.lstrip('/')), | ||
json.dumps(blame_info)) | ||
|
||
return any(v for v in file_blame_info.values()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters