Repository: DavidVujic/python-polylith

Issue:
Allow diff from commit hash
**Is your feature request related to a problem? Please describe.**
The organization forces us to work with the branches and have four eyes review. We are looking to optimize for branch development testing time and would like to run only tests for modules that have changed since the branch was taken.

**Describe the solution you'd like**
We want to extend the  ```poly diff --since ``` to support hash or short hash as well as tag. 

**Describe alternatives you've considered**
We could create a new parameter named --hash for which we could pass the custom hash.

**Additional context**
This is to optimize branch development on a repository, for which testing is time-consuming.


Discussion:

Patch:
diff --git a/components/polylith/commands/diff.py b/components/polylith/commands/diff.py
index 501472b..abac0ed 100644
--- a/components/polylith/commands/diff.py
+++ b/components/polylith/commands/diff.py
@@ -99,10 +99,10 @@ def print_views(root: Path, tag: str, options: dict) -> None:
 def run(tag_name: Union[str, None], options: dict):
     root = repo.get_workspace_root(Path.cwd())
 
-    tag = diff.collect.get_latest_tag(root, tag_name)
+    tag = diff.collect.get_latest_tag(root, tag_name) or tag_name
 
     if not tag:
-        print("No tags found in repository.")
+        print("No matching tags or commits found in repository.")
         return
 
     print_views(root, tag, options)
diff --git a/components/polylith/configuration/core.py b/components/polylith/configuration/core.py
index f327fed..0e6d95e 100644
--- a/components/polylith/configuration/core.py
+++ b/components/polylith/configuration/core.py
@@ -29,12 +29,15 @@ def get_git_tag_pattern(toml: dict) -> str:
     return toml["tool"]["polylith"]["git_tag_pattern"]
 
 
-def get_tag_pattern_from_config(path: Path, key: Union[str, None]) -> str:
+def get_tag_pattern_from_config(path: Path, key: Union[str, None]) -> Union[str, None]:
     toml: dict = _load_workspace_config(path)
 
     patterns = toml["tool"]["polylith"].get("tag", {}).get("patterns")
 
-    return patterns[key or "stable"] if patterns else get_git_tag_pattern(toml)
+    if not key:
+        return patterns["stable"] if patterns else get_git_tag_pattern(toml)
+
+    return patterns.get(key)
 
 
 def get_tag_sort_options_from_config(path: Path) -> List[str]:
diff --git a/components/polylith/diff/collect.py b/components/polylith/diff/collect.py
index 1a5dd86..e6571aa 100644
--- a/components/polylith/diff/collect.py
+++ b/components/polylith/diff/collect.py
@@ -58,6 +58,10 @@ def get_changed_projects(changed_files: List[Path]) -> list:
 
 def get_latest_tag(root: Path, key: Union[str, None]) -> Union[str, None]:
     tag_pattern = configuration.get_tag_pattern_from_config(root, key)
+
+    if not tag_pattern:
+        return None
+
     sorting_options = [
         f"--sort={option}"
         for option in configuration.get_tag_sort_options_from_config(root)
diff --git a/components/polylith/diff/report.py b/components/polylith/diff/report.py
index a635f78..01b1d10 100644
--- a/components/polylith/diff/report.py
+++ b/components/polylith/diff/report.py
@@ -86,7 +86,7 @@ def print_projects_affected_by_changes(projects: Set[str], short: bool) -> None:
 def print_diff_summary(tag: str, bases: List[str], components: List[str]) -> None:
     console = Console(theme=theme.poly_theme)
 
-    console.print(Padding(f"[data]Diff: based on the {tag} tag[/]", (1, 0, 1, 0)))
+    console.print(Padding(f"[data]Diff: based on {tag}[/]", (1, 0, 1, 0)))
 
     if not bases and not components:
         console.print("[data]No brick changes found.[/]")
diff --git a/projects/pdm_polylith_bricks/pyproject.toml b/projects/pdm_polylith_bricks/pyproject.toml
index f1cddee..25ba92d 100644
--- a/projects/pdm_polylith_bricks/pyproject.toml
+++ b/projects/pdm_polylith_bricks/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "pdm-polylith-bricks"
-version = "1.0.7"
+version = "1.0.8"
 description = "a PDM build hook for Polylith"
 authors = ["David Vujic"]
 homepage = "https://davidvujic.github.io/python-polylith-docs/"
diff --git a/projects/pdm_polylith_workspace/pyproject.toml b/projects/pdm_polylith_workspace/pyproject.toml
index ae3f86a..a422dc4 100644
--- a/projects/pdm_polylith_workspace/pyproject.toml
+++ b/projects/pdm_polylith_workspace/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "pdm-polylith-workspace"
-version = "1.0.7"
+version = "1.0.8"
 description = "a PDM build hook for a Polylith workspace"
 homepage = "https://davidvujic.github.io/python-polylith-docs/"
 repository = "https://github.com/davidvujic/python-polylith"
diff --git a/projects/poetry_polylith_plugin/pyproject.toml b/projects/poetry_polylith_plugin/pyproject.toml
index f1afe9e..1de34a6 100644
--- a/projects/poetry_polylith_plugin/pyproject.toml
+++ b/projects/poetry_polylith_plugin/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "poetry-polylith-plugin"
-version = "1.28.0"
+version = "1.29.0"
 description = "A Poetry plugin that adds tooling support for the Polylith Architecture"
 authors = ["David Vujic"]
 homepage = "https://davidvujic.github.io/python-polylith-docs/"
diff --git a/projects/polylith_cli/pyproject.toml b/projects/polylith_cli/pyproject.toml
index 1d07e0a..4567f39 100644
--- a/projects/polylith_cli/pyproject.toml
+++ b/projects/polylith_cli/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "polylith-cli"
-version = "1.16.0"
+version = "1.17.0"
 description = "Python tooling support for the Polylith Architecture"
 authors = ['David Vujic']
 homepage = "https://davidvujic.github.io/python-polylith-docs/"


Test patch:
diff --git a/test/components/polylith/configuration/test_core.py b/test/components/polylith/configuration/test_core.py
index 4df0d78..05295ca 100644
--- a/test/components/polylith/configuration/test_core.py
+++ b/test/components/polylith/configuration/test_core.py
@@ -74,6 +74,8 @@ def test_get_tag_pattern(use_loose):
     assert stable == "stable-*"
     assert release == "v[0-9]*"
 
+    assert core.get_tag_pattern_from_config(fake_path, "non_existing_tag") is None
+
 
 def test_get_tag_sort_options_from_config(use_loose):
     options = core.get_tag_sort_options_from_config(fake_path)