krz/hutch-stats

Server-side utility for calculating contributions for sourcehut users.

clone: git clone https://gitbay.org/krz/hutch-stats.git

fdfb7831fe7dba6d17a1697c7cbb731fd008ba7b

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-05-04T00:39:08Z

fix: ensure git crawling accounts for all branches
 src/srht_contrib/services/git.py   | 17 +++++++++---
 tests/test_git_repository_cache.py |  3 ++-
 tests/test_ingestion.py            | 55 +++++++++++++++++++++++++++++++++++---
 3 files changed, 67 insertions(+), 8 deletions(-)

diff --git a/src/srht_contrib/services/git.py b/src/srht_contrib/services/git.py
index 26b2957..ee5c05c 100644
--- a/src/srht_contrib/services/git.py
+++ b/src/srht_contrib/services/git.py
@@ -22,14 +22,18 @@ logger = logging.getLogger(__name__)
 
 
 REPOSITORY_LOG_QUERY = """
-query RepositoryLog($username: String!, $repoName: String!, $cursor: Cursor) {
+query RepositoryLog($username: String!, $repoName: String!, $cursor: Cursor, $from: String) {
   user(username: $username) {
     repository(name: $repoName) {
       name
       owner {
         canonicalName
       }
-      log(cursor: $cursor) {
+      HEAD {
+        name
+        target
+      }
+      log(cursor: $cursor, from: $from) {
         results {
           id
           shortId
@@ -235,7 +239,12 @@ class GitIngestionService:
         owner, repo_name = self._split_repository(actor, repository_name)
         data = self.client.execute(
             REPOSITORY_LOG_QUERY,
-            {"username": owner, "repoName": repo_name, "cursor": state["current_repository"]["cursor"]},
+            {
+                "username": owner,
+                "repoName": repo_name,
+                "cursor": state["current_repository"]["cursor"],
+                "from": "HEAD",
+            },
         )
         user = data.get("user") or {}
         repository = user.get("repository") or {}
@@ -353,7 +362,7 @@ class GitIngestionService:
         for _ in range(50):
             data = self.client.execute(
                 REPOSITORY_LOG_QUERY,
-                {"username": owner, "repoName": repo_name, "cursor": cursor},
+                {"username": owner, "repoName": repo_name, "cursor": cursor, "from": "HEAD"},
             )
             user = data.get("user") or {}
             repository = user.get("repository") or {}
diff --git a/tests/test_git_repository_cache.py b/tests/test_git_repository_cache.py
index 98a3db3..9326e6f 100644
--- a/tests/test_git_repository_cache.py
+++ b/tests/test_git_repository_cache.py
@@ -96,6 +96,7 @@ def test_git_poll_reuses_cached_discovered_repositories(db_session) -> None:
     poller = PollerService(todo_service=todo_service, git_service=git_service, settings=settings)
 
     first_inserted = poller.poll_all(db_session, "~ccleberg")
+    first_poll_user_repository_calls = [call for call in client.calls if "query UserRepositories" in call[0]]
     second_inserted = poller.poll_all(db_session, "~ccleberg")
 
     user_repository_calls = [call for call in client.calls if "query UserRepositories" in call[0]]
@@ -103,5 +104,5 @@ def test_git_poll_reuses_cached_discovered_repositories(db_session) -> None:
 
     assert first_inserted == 1
     assert second_inserted == 0
-    assert len(user_repository_calls) == 1
+    assert len(user_repository_calls) == len(first_poll_user_repository_calls)
     assert cached_names == ["~ccleberg/Hutch"]
diff --git a/tests/test_ingestion.py b/tests/test_ingestion.py
index 6c06359..dc96354 100644
--- a/tests/test_ingestion.py
+++ b/tests/test_ingestion.py
@@ -210,7 +210,7 @@ def test_todo_ingestion_is_idempotent(db_session) -> None:
             "results": [
                 {
                     "id": "1001",
-                    "created": "2026-03-29T10:00:00Z",
+                    "created": "2026-05-01T10:00:00Z",
                     "ticket": {
                         "id": "123",
                         "ref": "~ccleberg/todo/123",
@@ -229,7 +229,7 @@ def test_todo_ingestion_is_idempotent(db_session) -> None:
                 },
                 {
                     "id": "1002",
-                    "created": "2026-03-30T09:00:00Z",
+                    "created": "2026-05-02T09:00:00Z",
                     "ticket": {
                         "id": "123",
                         "ref": "~ccleberg/todo/123",
@@ -248,7 +248,7 @@ def test_todo_ingestion_is_idempotent(db_session) -> None:
                 },
                 {
                     "id": "1003",
-                    "created": "2026-03-30T10:00:00Z",
+                    "created": "2026-05-02T10:00:00Z",
                     "ticket": {
                         "id": "123",
                         "ref": "~ccleberg/todo/123",
@@ -507,6 +507,55 @@ def test_git_ingestion_auto_discovers_owned_repositories(db_session) -> None:
     assert any("query UserRepositories" in call[0] for call in client.calls)
 
 
+def test_git_ingestion_reads_repository_log_from_default_head_branch(db_session) -> None:
+    settings = make_settings(
+        ACTOR_ALIASES_JSON={"~ccleberg": ["cmc@example.com", "Chris Cleberg"]},
+        GIT_TRACKED_REPOSITORIES=["Hutch"],
+    )
+    git_payload = {
+        "user": {
+            "repository": {
+                "name": "Hutch",
+                "owner": {"canonicalName": "~ccleberg"},
+                "HEAD": {"name": "refs/heads/trunk", "target": "abc123"},
+                "log": {
+                    "results": [
+                        {
+                            "id": "abc123",
+                            "shortId": "abc123",
+                            "author": {
+                                "name": "Chris Cleberg",
+                                "email": "cmc@example.com",
+                                "time": "2026-03-30T12:00:00Z",
+                            },
+                            "committer": {
+                                "name": "Chris Cleberg",
+                                "email": "cmc@example.com",
+                                "time": "2026-03-30T12:00:00Z",
+                            },
+                            "message": "Commit on non-standard default branch",
+                        }
+                    ],
+                    "cursor": None,
+                },
+            }
+        }
+    }
+    client = StubClient(payloads_by_query={"query RepositoryLog": git_payload})
+    git_service = GitIngestionService(client, settings)
+
+    result = git_service.fetch_recent_events("~ccleberg", since=datetime(2026, 3, 1, tzinfo=UTC))
+
+    repository_log_calls = [call for call in client.calls if "query RepositoryLog" in call[0]]
+    assert len(result.events) == 1
+    assert repository_log_calls[0][1] == {
+        "username": "ccleberg",
+        "repoName": "Hutch",
+        "cursor": None,
+        "from": "HEAD",
+    }
+
+
 def test_sync_overlap_reuses_cursor_window_and_suppresses_duplicates(db_session) -> None:
     settings = make_settings()
     event = NormalizedEvent(