From 9c5f7c76719e07845da74c7ef915388b44722433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Mon, 20 Nov 2023 16:33:46 +0800 Subject: [PATCH] feat: +annotation --- metagpt/utils/git_repository.py | 51 +++++++++++++++++++++- tests/metagpt/utils/test_git_repository.py | 25 ++++------- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/metagpt/utils/git_repository.py b/metagpt/utils/git_repository.py index fd9794a80..c5b510612 100644 --- a/metagpt/utils/git_repository.py +++ b/metagpt/utils/git_repository.py @@ -30,12 +30,31 @@ class ChangeType(Enum): class GitRepository: + """A class representing a Git repository. + + :param local_path: The local path to the Git repository. + :param auto_init: If True, automatically initializes a new Git repository if the provided path is not a Git repository. + + Attributes: + _repository (Repo): The GitPython `Repo` object representing the Git repository. + """ + def __init__(self, local_path=None, auto_init=True): + """Initialize a GitRepository instance. + + :param local_path: The local path to the Git repository. + :param auto_init: If True, automatically initializes a new Git repository if the provided path is not a Git repository. + """ self._repository = None if local_path: self.open(local_path=local_path, auto_init=auto_init) def open(self, local_path: Path, auto_init=False): + """Open an existing Git repository or initialize a new one if auto_init is True. + + :param local_path: The local path to the Git repository. + :param auto_init: If True, automatically initializes a new Git repository if the provided path is not a Git repository. + """ if self.is_git_dir(local_path): self._repository = Repo(local_path) return @@ -45,9 +64,17 @@ class GitRepository: return self._init(local_path) def _init(self, local_path: Path): + """Initialize a new Git repository at the specified path. + + :param local_path: The local path where the new Git repository will be initialized. + """ self._repository = Repo.init(path=local_path) def add_change(self, files: Dict): + """Add or remove files from the staging area based on the provided changes. + + :param files: A dictionary where keys are file paths and values are instances of ChangeType. + """ if not self.is_valid or not files: return @@ -55,16 +82,24 @@ class GitRepository: self._repository.index.remove(k) if v is ChangeType.DELETED else self._repository.index.add([k]) def commit(self, comments): + """Commit the staged changes with the given comments. + + :param comments: Comments for the commit. + """ if self.is_valid: self._repository.index.commit(comments) def delete_repository(self): - # Delete the repository directory + """Delete the entire repository directory.""" if self.is_valid: shutil.rmtree(self._repository.working_dir) @property def changed_files(self) -> Dict[str, str]: + """Return a dictionary of changed files and their change types. + + :return: A dictionary where keys are file paths and values are change types. + """ files = {i: ChangeType.UNTRACTED for i in self._repository.untracked_files} changed_files = {f.a_path: ChangeType(f.change_type) for f in self._repository.index.diff(None)} files.update(changed_files) @@ -72,6 +107,11 @@ class GitRepository: @staticmethod def is_git_dir(local_path): + """Check if the specified directory is a Git repository. + + :param local_path: The local path to check. + :return: True if the directory is a Git repository, False otherwise. + """ git_dir = local_path / ".git" if git_dir.exists() and is_git_dir(git_dir): return True @@ -79,16 +119,25 @@ class GitRepository: @property def is_valid(self): + """Check if the Git repository is valid (exists and is initialized). + + :return: True if the repository is valid, False otherwise. + """ return bool(self._repository) @property def status(self) -> str: + """Return the Git repository's status as a string.""" if not self.is_valid: return "" return self._repository.git.status() @property def workdir(self) -> Path | None: + """Return the path to the working directory of the Git repository. + + :return: The path to the working directory or None if the repository is not valid. + """ if not self.is_valid: return None return Path(self._repository.working_dir) diff --git a/tests/metagpt/utils/test_git_repository.py b/tests/metagpt/utils/test_git_repository.py index 2e15f44f9..fa329a2ec 100644 --- a/tests/metagpt/utils/test_git_repository.py +++ b/tests/metagpt/utils/test_git_repository.py @@ -12,9 +12,7 @@ async def mock_file(filename, content=""): await file.write(content) -@pytest.mark.asyncio -async def test_git(): - local_path = Path(__file__).parent / "git" +async def mock_repo(local_path) -> (GitRepository, Path): if local_path.exists(): shutil.rmtree(local_path) assert not local_path.exists() @@ -28,6 +26,13 @@ async def test_git(): subdir = local_path / "subdir" subdir.mkdir(parents=True, exist_ok=True) await mock_file(subdir / "c.txt") + return repo, subdir + + +@pytest.mark.asyncio +async def test_git(): + local_path = Path(__file__).parent / "git" + repo, subdir = await mock_repo(local_path) assert len(repo.changed_files) == 3 repo.add_change(repo.changed_files) @@ -54,19 +59,7 @@ async def test_git(): @pytest.mark.asyncio async def test_git1(): local_path = Path(__file__).parent / "git1" - if local_path.exists(): - shutil.rmtree(local_path) - assert not local_path.exists() - repo = GitRepository(local_path=local_path, auto_init=True) - assert local_path.exists() - assert local_path == repo.workdir - assert not repo.changed_files - - await mock_file(local_path / "a.txt") - await mock_file(local_path / "b.txt") - subdir = local_path / "subdir" - subdir.mkdir(parents=True, exist_ok=True) - await mock_file(subdir / "c.txt") + await mock_repo(local_path) repo1 = GitRepository(local_path=local_path, auto_init=False) assert repo1.changed_files