diff --git a/src/wily/archivers/git.py b/src/wily/archivers/git.py index 71e8b802..dedeed27 100644 --- a/src/wily/archivers/git.py +++ b/src/wily/archivers/git.py @@ -4,6 +4,7 @@ Implementation of the archiver API for the gitpython module. """ import logging +from pathlib import Path from typing import Any, Dict, List, Tuple import git.exc @@ -195,3 +196,32 @@ def find(self, search: str) -> Revision: modified_files=modified_files, deleted_files=deleted_files, ) + + def is_data_outdated(self, filename: str, key: str) -> bool: + """ + Check whether file contents match between revision and working directory. + + :param filename: The name of the file to check. + :param key: The revision to which compare file contents. + :return: Whether the working directory copy of the file is outdated. + """ + commit = self.repo.rev_parse(key) + filepath = str(Path(self.repo.working_dir) / filename) + diff = commit.diff(None, filepath) + return bool(diff and diff[0].change_type == "M") + + def get_file_contents(self, rev_key: str, filename: str) -> str: + """ + Get contents of a file in a given git revision. + + :param rev_key: The key for the revision at which to fetch the file. + :param filename: The name of the file for which to get the contents. + :return: The file contents. + """ + git_filename = filename.replace("\\", "/") + contents = self.repo.git.execute( + ["git", "show", f"{rev_key}:{git_filename}"], + as_process=False, + stdout_as_string=True, + ) + return str(contents) diff --git a/test/integration/test_archiver.py b/test/integration/test_archiver.py index 693c5a72..8c300bc2 100644 --- a/test/integration/test_archiver.py +++ b/test/integration/test_archiver.py @@ -1,6 +1,7 @@ import pathlib import pytest +from git.exc import GitCommandError from git.repo.base import Repo from git.util import Actor @@ -64,6 +65,24 @@ def test_git_end_to_end(tmpdir): assert (tmppath / "test.py").exists() + # Test that outdated file contents are detected + archiver2 = GitArchiver(config) + outdated = archiver2.is_data_outdated("test.py", "HEAD") + assert not outdated + with open(tmppath / "test.py", "w") as file2: + file2.write("print(2)") + outdated = archiver2.is_data_outdated("test.py", "HEAD") + assert outdated + + # Test that file contents can be fetched for different revisions + content = archiver2.get_file_contents("HEAD", "test.py") + assert content == "print(1)" + with pytest.raises(GitCommandError): + # Error if file is not known in revision + content = archiver2.get_file_contents("HEAD~1", "test.py") + content = archiver2.get_file_contents("HEAD~1", ".gitignore") + assert content == ".wily/" + def test_dirty_git(tmpdir): """Check that repository fails to initialise if unchecked files are in the repo"""