From 8f4d8bccbd2340c9d430711c2cce06094fa5cd45 Mon Sep 17 00:00:00 2001 From: Douglas Lassance Date: Fri, 4 Oct 2024 17:20:41 +0200 Subject: [PATCH] Fix tests --- gitalong/batch.py | 69 +++++++++++++++++++++++++++--------------- gitalong/cli.py | 11 +++---- gitalong/repository.py | 21 ++++++++----- tests/cases.py | 18 ++++++----- tests/test_example.py | 32 ++++++++------------ 5 files changed, 87 insertions(+), 64 deletions(-) diff --git a/gitalong/batch.py b/gitalong/batch.py index 81e5cda..96c2ddc 100644 --- a/gitalong/batch.py +++ b/gitalong/batch.py @@ -26,7 +26,7 @@ async def get_files_last_commits( for filename in filenames: last_commit = {} - repository = Repository.from_filename(filename) + repository = Repository.from_filename(os.path.dirname(filename)) if not repository: last_commits.append(last_commit) continue @@ -114,20 +114,24 @@ async def get_commits_branches(commits: List[dict], remote: bool = False) -> Lis branches_list = [] tasks = [] for commit in commits: - args = ["git", "-C", commit.get("clone", ""), "--remote" if remote else []] + if "sha" not in commit: + branches_list.append([]) + continue + args = ["git", "-C", commit.get("clone", ""), "branch"] + if remote: + args += ["--remote"] args += ["--contains", commit.get("sha", "")] tasks.append(run_command(args)) - results = await asyncio.gather(*tasks) - for result in results: - stdout = result[0] - branches = stdout.decode("utf-8").split("\n") - branches = branches.replace("*", "") - branches = branches.replace(" ", "") - branches = branches.split("\n") if branches else [] - branch_names = set() - for branch in branches: - branch_names.add(branch.split("/")[-1]) - branches_list.append(list(branch_names)) + results = await asyncio.gather(*tasks) + for result in results: + branches = result.split("\n")[0] + branches = branches.replace("*", "") + branches = branches.replace(" ", "") + branches = branches.split("\n") if branches else [] + branch_names = set() + for branch in branches: + branch_names.add(branch.split("/")[-1]) + branches_list.append(list(branch_names)) return branches_list @@ -169,18 +173,34 @@ async def get_commit_changes(commits: List[git.Commit | dict]) -> List[str]: changes_list.append(commit.get("changes", [])) continue working_dir = commit.repo.working_dir - args = ["git", "-C", working_dir, "diff", "--numstat", "--no-renames"] - parent = commit.parents[0].hexsha if commit.parents else "" - if parent: - args += [f"{commit.hexsha}^", parent] + if not commit.parents: + # First commit, use git show + args = [ + "git", + "-C", + working_dir, + "show", + "--pretty=format:", + "--name-only", + commit.hexsha, + ] else: - args += [commit.hexsha] - args += ["--"] + # Subsequent commits, use git diff-tree + args = [ + "git", + "-C", + working_dir, + "diff-tree", + "--no-commit-id", + "--name-only", + "-r", + commit.hexsha, + ] tasks.append(run_command(args)) results = await asyncio.gather(*tasks) for result in results: - stdout = result[0] - changes = stdout.decode("utf-8").split("\n") + changes = result.split("\n") + changes = [change for change in changes if change] changes_list.append(changes) return changes_list @@ -207,6 +227,7 @@ async def get_commits_dicts(commits: List[git.Commit | dict]) -> List[dict]: "changes": changes, "date": str(commit.committed_datetime), "author": commit.author.name, + "clone": commit.repo.working_dir, } ) return commit_dicts @@ -221,9 +242,7 @@ async def claim_files( Also makes the files writable if the configured is set to affect permissions. Args: - filename (str): - The file to make writable. Takes a path that's absolute or relative to - the managed repository. + filename (str): The absolute filename to the file to claim. prune (bool, optional): Prune branches if a fetch is necessary. Returns: @@ -234,7 +253,7 @@ async def claim_files( for filename in filenames: last_commits = await get_files_last_commits([filename], prune=prune) last_commit = last_commits[0] - repository = Repository.from_filename(filename) + repository = Repository.from_filename(os.path.dirname(filename)) config = repository.config if repository else {} modify_permissions = config.get("modify_permissions") spread = repository.get_commit_spread(last_commit) if repository else 0 diff --git a/gitalong/cli.py b/gitalong/cli.py index 05c02b1..2cc04df 100644 --- a/gitalong/cli.py +++ b/gitalong/cli.py @@ -75,12 +75,8 @@ def config(ctx, prop): # pylint: disable=missing-function-docstring "files their permissions changed." ) ) -@click.argument( - "repository", - # help="The path to the repository to update." -) @click.pass_context -def update(ctx, repository): +def update(ctx): """TODO: Improve error handling.""" repository = Repository.from_filename(ctx.obj.get("REPOSITORY", "")) if not repository: @@ -122,7 +118,10 @@ def update(ctx, repository): "-p", "--profile", is_flag=True, - help="Will generate a profile file in the current workin directory.", + help=( + "Will generate a profile file in the current workin directory." + "The file can be open in a profiler like snakeviz." + ), ) @click.pass_context def status(ctx, filename, profile=False): # pylint: disable=missing-function-docstring diff --git a/gitalong/repository.py b/gitalong/repository.py index 351f0cd..9a69735 100644 --- a/gitalong/repository.py +++ b/gitalong/repository.py @@ -16,9 +16,6 @@ from git.repo import Repo -# Deliberatedly import the module to avoid circular imports. -from . import batch - from .store import Store from .enums import CommitSpread from .stores.git_store import GitStore @@ -166,8 +163,8 @@ def from_filename(cls, filename: str) -> Optional["Repository"]: """ Args: filename (str): - The absolute path to the clone or either the relative or - absolute path to one of its files. + Existing absolute path to a file or folder in the managed repository. + That inclused the managed repository itself. Returns: Optional[Repository]: The repository or None. @@ -433,10 +430,10 @@ def _accumulate_local_only_commits(self, start: git.Commit, local_commits: list) if self._managed_repository.git.branch("--remotes", "--contains", start.hexsha): return # TODO: These call to batch functions are expensive for a single file. - commits = asyncio.run(batch.get_commits_dicts([start])) + commits = asyncio.run(self.batch.get_commits_dicts([start])) commit = commits[0] if commits else {} commit.update(self.context_dict) - branches_list = asyncio.run(batch.get_commits_branches([commit])) + branches_list = asyncio.run(self.batch.get_commits_branches([commit])) branches = branches_list[0] if branches_list else [] commit["branches"] = {"local": branches} # Maybe we should compare the SHA here. @@ -658,3 +655,13 @@ def git(self) -> git.Git: git.cmd.Git: The Git command line interface for the managed repository. """ return self._managed_repository.git + + @property + def batch(self): + """ + Returns: + Batch: The batch object for the managed repository. + """ + from . import batch # pylint: disable=import-outside-toplevel + + return batch diff --git a/tests/cases.py b/tests/cases.py index 15348b0..32604a5 100644 --- a/tests/cases.py +++ b/tests/cases.py @@ -15,7 +15,9 @@ from gitalong import Repository, CommitSpread, RepositoryNotSetup, cli from gitalong.functions import is_read_only -from gitalong.batch import claim_files, get_files_last_commits + +# Deliberatedly import the module to avoid circular imports. +import gitalong.batch as batch from .functions import save_image @@ -86,14 +88,14 @@ def test_lib(self): self.assertEqual(1, len(local_only_commits)) self.assertEqual(3, len(local_only_commits[0]["changes"])) - image_path = os.path.join(working_dir, "staged_image_02.jpg") - save_image(image_path) + staged_image_02_path = os.path.join(working_dir, "staged_image_02.jpg") + save_image(staged_image_02_path) # Simulating the application syncing when saving the file. self.repository.update_tracked_commits() # print("POST-SAVE TRACKED COMMITS") # pprint(self.repository.get_tracked_commits()) - self._managed_clone.index.add(image_path) + self._managed_clone.index.add(staged_image_02_path) self._managed_clone.index.commit(message="Add staged_image_02.jpg") # Simulating the post-commit hook. self.repository.update_tracked_commits() @@ -108,7 +110,7 @@ def test_lib(self): # pprint(self.repository.get_tracked_commits()) # We just pushed the changes therefore there should be no missing commit. - last_commits = asyncio.run(get_files_last_commits(["staged_image_02.jpg"])) + last_commits = asyncio.run(batch.get_files_last_commits([staged_image_02_path])) last_commit = last_commits[0] if last_commits else {} spread = self.repository.get_commit_spread(last_commit) self.assertEqual( @@ -124,7 +126,7 @@ def test_lib(self): # pprint(self.repository.get_tracked_commits()) # As a result it should be a commit we do no have locally. - last_commits = asyncio.run(get_files_last_commits(["staged_image_02.jpg"])) + last_commits = asyncio.run(batch.get_files_last_commits([staged_image_02_path])) last_commit = last_commits[0] if last_commits else {} spread = self.repository.get_commit_spread(last_commit) self.assertEqual(CommitSpread.REMOTE_MATCHING_BRANCH, spread) @@ -138,7 +140,9 @@ def test_lib(self): self.repository.update_tracked_commits() - claims = asyncio.run(claim_files([staged_image_01_path, image_path])) + claims = asyncio.run( + batch.claim_files([staged_image_01_path, staged_image_02_path]) + ) missing_commit = claims[0] self.assertEqual(False, bool(missing_commit)) missing_commit = claims[1] diff --git a/tests/test_example.py b/tests/test_example.py index 94e42dd..3e902f7 100644 --- a/tests/test_example.py +++ b/tests/test_example.py @@ -6,8 +6,6 @@ from git.repo import Repo from gitalong import Repository, RepositoryNotSetup, CommitSpread -from gitalong.batch import claim_files, get_files_last_commits - def test_example(): """Testing example code featured in README.md.""" @@ -44,20 +42,14 @@ def test_example(): ) # Creating some files. - open( - os.path.join(project_clone.working_dir, "uncommitted.png"), - "w", - encoding="utf-8", - ).close() - open( - os.path.join(project_clone.working_dir, "local.gif"), "w", encoding="utf-8" - ).close() - open( - os.path.join(project_clone.working_dir, "remote.jpg"), "w", encoding="utf-8" - ).close() - open( - os.path.join(project_clone.working_dir, "untracked.txt"), "w", encoding="utf-8" - ).close() + uncomitted = os.path.join(project_clone.working_dir, "uncommitted.png") + local = os.path.join(project_clone.working_dir, "local.gif") + remote = os.path.join(project_clone.working_dir, "remote.jpg") + untracked = os.path.join(project_clone.working_dir, "untracked.txt") + open(uncomitted, "w", encoding="utf-8").close() + open(local, "w", encoding="utf-8").close() + open(remote, "w", encoding="utf-8").close() + open(untracked, "w", encoding="utf-8").close() # Spreading them across branches. project_clone.index.add("untracked.txt") @@ -74,14 +66,16 @@ def test_example(): repository.update_tracked_commits() # Update permissions of all files based on track commits. Because - # `modify_permssions` was passed this will update all permissions of tracked files. + # `modify_permissions` was passed this will update all permissions of tracked files. # Permission updates currently comes at high performance cost and is not # recommended. locally_changed_files = repository.locally_changed_files for filename in repository.files: repository.update_file_permissions(filename, locally_changed_files) - last_commits = asyncio.run(get_files_last_commits(repository.files)) + last_commits = asyncio.run( + repository.batch.get_files_last_commits([uncomitted, local, remote, untracked]) + ) # Now we'll get the last commit for our files. # This could return a dummy commit representing uncommitted changes. @@ -106,7 +100,7 @@ def test_example(): # Trying to claim the files. claims = asyncio.run( - claim_files(["uncommitted.png", "local.gif", "remote.jpg", "untracked.txt"]) + repository.batch.claim_files([uncomitted, local, remote, untracked]) ) assert bool(claims[0]) is False