coveralls: ## Pull Request Test Coverage Report for [Build 35](https://coveralls.io/builds/19797952)

* **15** of **16**   **(93.75%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage decreased (**-1.0%**) to **99.0%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [gitsecret/__init__.py](https://coveralls.io/builds/19797952/source?filename=gitsecret%2F__init__.py#L171) | 15 | 16 | 93.75%
<!-- | **Total:** | **15** | **16** | **93.75%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/19797952/badge)](https://coveralls.io/builds/19797952) |
| :-- | --: |
| Change from base [Build 33](https://coveralls.io/builds/19794776): |  -1.0% |
| Covered Lines: | 99 |
| Relevant Lines: | 100 |

---
##### 💛  - [Coveralls](https://coveralls.io)

diff --git a/gitsecret/__init__.py b/gitsecret/__init__.py
index df097f2..12409d9 100644
--- a/gitsecret/__init__.py
+++ b/gitsecret/__init__.py
@@ -1,7 +1,7 @@
 import re
 import shlex
 import subprocess
-from typing import Optional, Tuple
+from typing import Optional, Tuple, List
 import git
 
 
@@ -18,7 +18,7 @@ class GitSecret():
         self.git_repo = git.Repo(self.repo_path)
 
     def _command_and_parse(self,
-                           command: list,
+                           command: list,  # pylint: disable=E0601
                            regex: Optional[str] = None) -> Tuple[subprocess.CompletedProcess, list]:
         """
         A (inflexible, implementation-focused) centralized method to run a method and optionally applies a regex to the
@@ -144,7 +144,31 @@ class GitSecret():
 
         self._command_and_parse(remove_command, remove_regex)
 
-    def clean(self) -> list:
+    def clean(self) -> Optional[List]:
         clean_command = shlex.split("git secret clean -v")
 
         return self._command_and_split(clean_command)[1:]
+
+    def list(self) -> Optional[List]:
+        list_command = shlex.split("git secret list")
+
+        return self._command_and_split(list_command)[:]
+
+    def changes(self, password: str, file_path: Optional[str] = None, gpg_path: Optional[str] = None) -> str:
+        changes_command = shlex.split("git secret changes")
+
+        if gpg_path:
+            changes_command.extend(["-d", gpg_path])
+
+        changes_command.extend(["-p", password])
+
+        if file_path:
+            changes_command.append(file_path)
+
+        output = subprocess.run(args=changes_command, capture_output=True, cwd=self.repo_path)
+
+        if output.returncode != 0:
+            raise GitSecretException("Error running git secret command.  stdout: %s; stderr: %s" %
+                                     (output.stdout.decode("utf-8"), output.stderr.decode("utf-8")))
+
+        return str(output.stdout.decode("utf-8"))

Implement git secret commands to show information about repo
Implement functionality & tests for:
- [x] git-secret-list
- [x] git-secret-changes
diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py
index f2f4681..67e5641 100644
--- a/tests/e2e/test_e2e.py
+++ b/tests/e2e/test_e2e.py
@@ -52,4 +52,7 @@ def test_gitsecret_files(gen_gitsecret):
     gitsecret.hide()
     gitsecret.reveal("test")
     # gitsecret.remove("hello.txt")
+    with open(os.path.join(PATH, "hello.txt"), "a") as test_file:
+        test_file.write("Hello again!")
+    assert "Hello again!" in gitsecret.changes("test")
     assert len(gitsecret.clean()) == 1
diff --git a/tests/unit/test_gitsecret_changes.py b/tests/unit/test_gitsecret_changes.py
new file mode 100644
index 0000000..1b8c02d
--- /dev/null
+++ b/tests/unit/test_gitsecret_changes.py
@@ -0,0 +1,78 @@
+import pytest
+import gitsecret
+from gitsecret import GitSecretException
+from tests.utilities.factories import FakeCompletedProcess
+from tests.utilities.fixtures import gen_gitsecret  # noqa: F401
+
+
+def test_gitsecret_changes(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes("test")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-p", "test"]
+
+
+def test_gitsecret_changes_path(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   file_path="/path/to/file")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-p", "test", "/path/to/file"]
+
+
+def test_gitsecret_changes_gpgpath(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   gpg_path="/path/to/key")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-d", "/path/to/key", "-p", "test"]
+
+
+def test_gitsecret_changes_all(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nchanges in /Users/ivanlee/repos/sandbox/integration/hello.txt:\n--- /dev/fd/63	2018-10-30 03:12:54.000000000 -0400\n+++ /Users/ivanlee/repos/sandbox/integration/hello.txt	2018-10-30 03:12:54.000000000 -0400\n@@ -1 +1 @@-Hello!\n+Hello!Hello again!\n\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert "Hello again!" in gen_gitsecret.changes(password="test",
+                                                   gpg_path="/path/to/key",
+                                                   file_path="/path/to/file")
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "changes", "-d", "/path/to/key", "-p", "test", "/path/to/file"]
+
+
+def test_gitsecret_changes_exception(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "None",
+        'stderr': "git-secret: abort: /Users/ivanlee/repos/sandbox/box5/.gitsecret/paths/mapping.cfg is missing",
+        'returncode': 1
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    with pytest.raises(GitSecretException):
+        gen_gitsecret.changes("test")
diff --git a/tests/unit/test_gitsecret_list.py b/tests/unit/test_gitsecret_list.py
new file mode 100644
index 0000000..5d65e5d
--- /dev/null
+++ b/tests/unit/test_gitsecret_list.py
@@ -0,0 +1,32 @@
+import pytest
+import gitsecret
+from gitsecret import GitSecretException
+from tests.utilities.factories import FakeCompletedProcess
+from tests.utilities.fixtures import gen_gitsecret  # noqa: F401
+
+
+def test_gitsecret_list(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "\nhello.txt\nhello1.txt\n",
+        'returncode': 0
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    assert gen_gitsecret.list() == ["hello.txt", "hello1.txt"]
+
+    assert gitsecret.subprocess.run.assert_called_once
+    assert gitsecret.subprocess.run.call_args[1]['args'] == ["git", "secret", "list"]
+
+
+def test_gitsecret_list_exception(gen_gitsecret, mocker):  # noqa: F811
+    shell_command = FakeCompletedProcess(**{
+        'stdout': "None",
+        'stderr': "git-secret: abort: /Users/ivanlee/repos/sandbox/box5/.gitsecret/paths/mapping.cfg is missing",
+        'returncode': 1
+    })
+
+    mocker.patch('gitsecret.subprocess.run', return_value=shell_command)
+
+    with pytest.raises(GitSecretException):
+        gen_gitsecret.list()

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-flake8 pytest-html pytest-runner pytest-rerunfailures pytest-xdist pytest-mock factory-boy coveralls mypy pylint bumpversion twine mkdocs
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==3.3.9
backports.tarfile==1.2.0
bump2version==1.0.1
bumpversion==0.6.0
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
coverage==7.8.0
coveralls==4.0.1
cryptography==44.0.2
dill==0.3.9
docopt==0.6.2
docutils==0.21.2
exceptiongroup==1.2.2
execnet==2.1.1
factory_boy==3.3.3
Faker==37.1.0
flake8==7.2.0
ghp-import==2.1.0
gitdb2==2.0.5
GitPython==2.1.11
-e git+https://github.com/ivanklee86/gitsecret.git@20698f616851eb3f6e74b1b84f5051d3c0e01a9d#egg=gitsecret
id==1.5.0
idna==3.10
importlib_metadata==8.6.1
iniconfig==2.1.0
isort==6.0.1
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
mergedeep==1.3.4
mkdocs==1.6.1
mkdocs-get-deps==0.2.0
more-itertools==10.6.0
mypy==1.15.0
mypy-extensions==1.0.0
nh3==0.2.21
packaging==24.2
pathspec==0.12.1
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pycparser==2.22
pyflakes==3.3.2
Pygments==2.19.1
pylint==3.3.6
pytest==8.3.5
pytest-cov==6.0.0
pytest-flake8==1.3.0
pytest-html==4.1.1
pytest-metadata==3.1.1
pytest-mock==3.14.0
pytest-rerunfailures==15.0
pytest-runner==6.0.1
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
PyYAML==6.0.2
pyyaml_env_tag==0.1
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
six==1.17.0
smmap2==2.0.5
tomli==2.2.1
tomlkit==0.13.2
twine==6.1.0
typing_extensions==4.13.0
tzdata==2025.2
urllib3==2.3.0
watchdog==6.0.0
zipp==3.21.0

name: gitsecret
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==3.3.9
      - backports-tarfile==1.2.0
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - certifi==2025.1.31
      - cffi==1.17.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - coverage==7.8.0
      - coveralls==4.0.1
      - cryptography==44.0.2
      - dill==0.3.9
      - docopt==0.6.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - factory-boy==3.3.3
      - faker==37.1.0
      - flake8==7.2.0
      - ghp-import==2.1.0
      - gitdb2==2.0.5
      - gitpython==2.1.11
      - id==1.5.0
      - idna==3.10
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - isort==6.0.1
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown==3.7
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - mergedeep==1.3.4
      - mkdocs==1.6.1
      - mkdocs-get-deps==0.2.0
      - more-itertools==10.6.0
      - mypy==1.15.0
      - mypy-extensions==1.0.0
      - nh3==0.2.21
      - packaging==24.2
      - pathspec==0.12.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.2
      - pygments==2.19.1
      - pylint==3.3.6
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - pytest-flake8==1.3.0
      - pytest-html==4.1.1
      - pytest-metadata==3.1.1
      - pytest-mock==3.14.0
      - pytest-rerunfailures==15.0
      - pytest-runner==6.0.1
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - pyyaml-env-tag==0.1
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - six==1.17.0
      - smmap2==2.0.5
      - tomli==2.2.1
      - tomlkit==0.13.2
      - twine==6.1.0
      - typing-extensions==4.13.0
      - tzdata==2025.2
      - urllib3==2.3.0
      - watchdog==6.0.0
      - zipp==3.21.0
prefix: /opt/conda/envs/gitsecret

tests/unit/test_gitsecret_changes.py::test_gitsecret_changes tests/unit/test_gitsecret_changes.py::test_gitsecret_changes_path tests/unit/test_gitsecret_changes.py::test_gitsecret_changes_gpgpath tests/unit/test_gitsecret_changes.py::test_gitsecret_changes_all tests/unit/test_gitsecret_changes.py::test_gitsecret_changes_exception tests/unit/test_gitsecret_list.py::test_gitsecret_list tests/unit/test_gitsecret_list.py::test_gitsecret_list_exception
tests/e2e/test_e2e.py::flake-8::FLAKE8 tests/unit/test_gitsecret_changes.py::flake-8::FLAKE8 tests/unit/test_gitsecret_list.py::flake-8::FLAKE8
gitsecret/__init__.py
gitsecret/__init__.py