git-pandas 2.5.0__tar.gz → 2.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/test-suite.yml +32 -7
  2. git_pandas-2.7.0/.obvious/codebase-map.md +18 -0
  3. git_pandas-2.7.0/.obvious/config.yml +6 -0
  4. git_pandas-2.7.0/.obvious/obvious.md +97 -0
  5. git_pandas-2.7.0/.obvious/skills/local-dev/SKILL.md +76 -0
  6. {git_pandas-2.5.0 → git_pandas-2.7.0}/CHANGELOG.md +69 -0
  7. {git_pandas-2.5.0 → git_pandas-2.7.0}/PKG-INFO +25 -18
  8. {git_pandas-2.5.0 → git_pandas-2.7.0}/README.md +15 -14
  9. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/cache.rst +2 -1
  10. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/index.rst +3 -0
  11. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/usecases.rst +7 -3
  12. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/project.py +81 -24
  13. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/repository.py +152 -35
  14. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/plotting.py +4 -3
  15. {git_pandas-2.5.0 → git_pandas-2.7.0}/pyproject.toml +13 -5
  16. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_advanced.py +1 -0
  17. git_pandas-2.7.0/tests/test_Project/test_cumulative_blame_output_shape.py +127 -0
  18. git_pandas-2.7.0/tests/test_Project/test_date_source.py +75 -0
  19. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_edge_cases.py +1 -1
  20. git_pandas-2.7.0/tests/test_Project/test_project_limit_allocation.py +278 -0
  21. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_punchcard.py +17 -0
  22. git_pandas-2.7.0/tests/test_Project/test_revs_num_datapoints.py +99 -0
  23. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_cumulative_blame_duplicate_dates.py +8 -1
  24. git_pandas-2.7.0/tests/test_Repository/test_cumulative_blame_output_shape.py +126 -0
  25. git_pandas-2.7.0/tests/test_Repository/test_date_source.py +130 -0
  26. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_edge_cases.py +1 -1
  27. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_error_handling.py +43 -0
  28. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_punchcard.py +19 -20
  29. git_pandas-2.7.0/tests/test_Repository/test_revs_num_datapoints.py +170 -0
  30. git_pandas-2.7.0/tests/test_by_validation.py +79 -0
  31. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_management.py +1 -1
  32. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cached_signatures.py +2 -0
  33. {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/project_overview.mdc +0 -0
  34. {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/pytest_standards.mdc +0 -0
  35. {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/python_standards.mdc +0 -0
  36. {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/sphinx_docs_standards.mdc +0 -0
  37. {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/testing.mdc +0 -0
  38. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/ISSUE_TEMPLATE.md +0 -0
  39. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  40. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/docs.yml +0 -0
  41. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/pypi-publish.yml +0 -0
  42. {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/test-docs-build.yml +0 -0
  43. {git_pandas-2.5.0 → git_pandas-2.7.0}/.gitignore +0 -0
  44. {git_pandas-2.5.0 → git_pandas-2.7.0}/CONTRIBUTING.md +0 -0
  45. {git_pandas-2.5.0 → git_pandas-2.7.0}/LICENSE.md +0 -0
  46. {git_pandas-2.5.0 → git_pandas-2.7.0}/Makefile +0 -0
  47. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/AUDIT_REPORT.md +0 -0
  48. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/Makefile +0 -0
  49. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/make.bat +0 -0
  50. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/requirements.txt +0 -0
  51. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/CNAME +0 -0
  52. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/_static/custom.css +0 -0
  53. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/conf.py +0 -0
  54. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/contributors.rst +0 -0
  55. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/performance.rst +0 -0
  56. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/project.rst +0 -0
  57. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/remote_operations.rst +0 -0
  58. {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/repository.rst +0 -0
  59. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/__init__.py +0 -0
  60. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/attributes.py +0 -0
  61. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/bus_analysis.py +0 -0
  62. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cache_management.py +0 -0
  63. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cache_timestamps.py +0 -0
  64. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cloud_repo.py +0 -0
  65. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/commit_history.py +0 -0
  66. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cumulative_blame.py +0 -0
  67. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/definitions.py +0 -0
  68. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/file_change_rates.py +0 -0
  69. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/hours_estimate.py +0 -0
  70. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/cumulative_blame.png +0 -0
  71. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/cumulative_project_blame.png +0 -0
  72. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/githubblame.png +0 -0
  73. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/lifeline.png +0 -0
  74. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/stravalib_cumulative_blame.png +0 -0
  75. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/lifeline.py +0 -0
  76. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/parallel_blame.py +0 -0
  77. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/project_blame.py +0 -0
  78. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/punchcard.py +0 -0
  79. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/release_analytics.py +0 -0
  80. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/remote_fetch_and_cache_warming.py +0 -0
  81. {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/repo_file_detail.py +0 -0
  82. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/__init__.py +0 -0
  83. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/cache.py +0 -0
  84. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/logging.py +0 -0
  85. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/__init__.py +0 -0
  86. {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/check_api.py +0 -0
  87. {git_pandas-2.5.0 → git_pandas-2.7.0}/img/cumulative_blame.png +0 -0
  88. {git_pandas-2.5.0 → git_pandas-2.7.0}/img/lifeline.png +0 -0
  89. {git_pandas-2.5.0 → git_pandas-2.7.0}/img/punchcard.png +0 -0
  90. {git_pandas-2.5.0 → git_pandas-2.7.0}/mcp_server/README.md +0 -0
  91. {git_pandas-2.5.0 → git_pandas-2.7.0}/mcp_server/server.py +0 -0
  92. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/__init__.py +0 -0
  93. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/conftest.py +0 -0
  94. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/__init__.py +0 -0
  95. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_blame_file_detail_empty.py +0 -0
  96. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_blame_multi_repo.py +0 -0
  97. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_bus_factor.py +0 -0
  98. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_commits_in_tags.py +0 -0
  99. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_default_branch_detection.py +0 -0
  100. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_error_handling.py +0 -0
  101. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_file_detail_author.py +0 -0
  102. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_github_profile.py +0 -0
  103. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_properties.py +0 -0
  104. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/__init__.py +0 -0
  105. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_advanced.py +0 -0
  106. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_bus_factor.py +0 -0
  107. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_commits_in_tags.py +0 -0
  108. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_change_history_values.py +0 -0
  109. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_detail.py +0 -0
  110. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_operations.py +0 -0
  111. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_owner_author.py +0 -0
  112. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_hours_estimate.py +0 -0
  113. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_properties.py +0 -0
  114. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_rev_to_rev.py +0 -0
  115. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_bulk_fetch_warm.py +0 -0
  116. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache.py +0 -0
  117. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_immutability.py +0 -0
  118. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_integration.py +0 -0
  119. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_key_consistency.py +0 -0
  120. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_threading.py +0 -0
  121. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_warming.py +0 -0
  122. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_examples.py +0 -0
  123. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_logging.py +0 -0
  124. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_mcp_serialization.py +0 -0
  125. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_remote_fetch.py +0 -0
  126. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_repository_cache.py +0 -0
  127. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_utilities/test_check_api.py +0 -0
  128. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_utilities/test_init.py +0 -0
  129. {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_utilities/test_plotting.py +0 -0
@@ -15,11 +15,26 @@ concurrency:
15
15
 
16
16
  jobs:
17
17
  test:
18
+ name: "test (py${{ matrix.python-version }}, pandas ${{ matrix.pandas-major }}.x)"
18
19
  runs-on: ubuntu-latest
19
20
  strategy:
21
+ fail-fast: false
20
22
  matrix:
21
- python-version: ['3.10', '3.11', '3.12']
22
-
23
+ # pandas 3 requires Python >= 3.11, so the 3.10 entry can only ever be pandas 2.
24
+ include:
25
+ - python-version: '3.10'
26
+ pandas-major: '2'
27
+ pandas-spec: '>=2.0,<3.0'
28
+ - python-version: '3.11'
29
+ pandas-major: '2'
30
+ pandas-spec: '>=2.0,<3.0'
31
+ - python-version: '3.12'
32
+ pandas-major: '2'
33
+ pandas-spec: '>=2.0,<3.0'
34
+ - python-version: '3.12'
35
+ pandas-major: '3'
36
+ pandas-spec: '>=3.0,<4.0'
37
+
23
38
  steps:
24
39
  - name: Checkout code
25
40
  uses: actions/checkout@v4
@@ -42,16 +57,26 @@ jobs:
42
57
  - name: Install dependencies
43
58
  run: |
44
59
  uv pip install --system -e ".[dev]"
45
-
60
+ uv pip install --system "pandas${{ matrix.pandas-spec }}"
61
+
62
+ # Fail loudly rather than silently testing the wrong major.
63
+ - name: Verify pandas version
64
+ run: |
65
+ python -c "
66
+ import pandas
67
+ print('pandas', pandas.__version__)
68
+ assert pandas.__version__.split('.')[0] == '${{ matrix.pandas-major }}', pandas.__version__
69
+ "
70
+
46
71
  - name: Configure Git
47
72
  run: |
48
73
  git config --global user.email "github-actions@github.com"
49
74
  git config --global user.name "GitHub Actions"
50
-
75
+
51
76
  - name: Run tests
52
77
  run: |
53
- MPLBACKEND=Agg uv run pytest -v -m "not slow"
54
-
78
+ MPLBACKEND=Agg python -m pytest -v -m "not slow"
79
+
55
80
  - name: Run linting
56
81
  run: |
57
- uv run ruff check .
82
+ python -m ruff check .
@@ -0,0 +1,18 @@
1
+ # git-pandas — Codebase Map
2
+
3
+ Folder-level overview (depth 2). `wdm0006/git-pandas`, default branch `master`, library v2.5.0.
4
+
5
+ | Path | What it is |
6
+ |---|---|
7
+ | `gitpandas/` | The library package. `repository.py` (~3.1k lines) — `Repository` class: single-repo metrics (commit history, blame, cumulative blame, bus factor, hours estimate, rev-to-rev, cache warming, remote fetch). `project.py` (~1.6k lines) — `ProjectDirectory` (multi-repo discovery + aggregation) and `GitHubProfile`. `cache.py` (~860 lines) — `EphemeralCache`, `DiskCache`, `RedisDFCache` + the `_string_handle_to_df` memoization decorator every metric goes through. `logging.py` — verbose-mode logger. |
8
+ | `gitpandas/utilities/` | `plotting.py` — chart helpers (punchcard, lifeline, cumulative blame); `check_api.py` — API-parity checker (CONTRIBUTING asks for Repository/ProjectDirectory parity). |
9
+ | `tests/` | pytest suite. `test_Repository/`, `test_Project/`, `test_utilities/` mirror the package; root-level files cover cache behavior (immutability, threading, warming, key consistency, management), remote fetch, MCP serialization (mocks the `mcp` import — runs without mcp installed), logging, examples smoke (`test_examples.py`, marked `slow`). `conftest.py` builds throwaway git repos in tmp dirs. |
10
+ | `examples/` | One runnable script per analysis (commit_history, bus_analysis, cumulative_blame, punchcard, …). Run from repo root: `make run-example example=<name>`. `definitions.py` locates the git-pandas checkout for self-analysis. |
11
+ | `mcp_server/` | FastMCP **stdio** server exposing gitpandas metrics as MCP tools. Own README. Needs `mcp[cli]<2` + uvicorn (PEP 723 block, not in extras). |
12
+ | `docs/` | Sphinx docs with their own `Makefile` and `requirements.txt`. |
13
+ | `.github/workflows/` | `test-suite.yml` — CI matrix py 3.10–3.12 × pandas 2/3, runs `pytest -m "not slow"` + `ruff check`; `test-docs-build.yml` — PR docs check; `docs.yml` — publishes docs from master; `pypi-publish.yml` — sdist to PyPI on release. |
14
+ | `.cursor/rules/` | Agent/editor guidance: python, pytest, sphinx-docs, testing standards + a project overview. |
15
+ | `img/`, `examples/img/` | Images used by README and examples. |
16
+
17
+ **Key invariants** (from CONTRIBUTING.md): maintain feature/API parity between `Repository` and
18
+ `ProjectDirectory`; sphinx-format docstrings; memory-heavy functions take a `limit` option.
@@ -0,0 +1,6 @@
1
+ # Repo policy for obvious workers.
2
+ # Auto-detected from GitHub repo settings (wdm0006/git-pandas) on 2026-09-06.
3
+ defaultBranch: master
4
+ # All three merge methods are enabled on this repo; squash keeps setup PRs to one commit.
5
+ mergeMethod: squash
6
+ requireHumanMerge: true
@@ -0,0 +1,97 @@
1
+ # git-pandas — Agent Guide
2
+
3
+ **Repo:** `wdm0006/git-pandas` · **Default branch:** `master` · **Language:** Python (library, v2.5.0)
4
+
5
+ git-pandas turns Git repository data into pandas DataFrames — a pure Python library with an
6
+ optional MCP stdio server. **No web app, no database, no listening ports, no long-running
7
+ services.** "Local dev is healthy" means: venv installed, test suite green, lint clean,
8
+ examples run.
9
+
10
+ ## Stack
11
+
12
+ - **Runtime:** Python ≥ 3.10 (CI matrix: 3.10 / 3.11 / 3.12 × pandas 2.x / 3.x)
13
+ - **Package manager:** uv (hatchling build backend; `uv.lock` is gitignored — resolution is fresh from `pyproject.toml`)
14
+ - **Core deps:** gitpython, numpy, pandas (≥2.0), requests
15
+ - **Tooling:** pytest (+ pytest-cov, pytest-mock), ruff (lint + format), sphinx, coverage
16
+ - **Parallelism:** joblib — ⚠️ only in the `dev` extra, **not** in `all`
17
+ - **Optional:** redis (RedisDFCache — its tests are fully mocked, no server needed), matplotlib / lifelines (examples)
18
+ - **MCP server:** `mcp[cli]<2` + uvicorn — in no extras group; declared only in the PEP 723 block of `mcp_server/server.py`
19
+
20
+ ## Environment (baked into the sandbox snapshot)
21
+
22
+ - `.venv/` at repo root: uv venv on CPython 3.12.14 (uv-managed), git-pandas 2.5.0 installed
23
+ editable with extras `dev` + `all`, plus `mcp[cli]<2` (1.29.1) and uvicorn
24
+ - uv 0.12.10 at `~/.local/bin` (add to PATH: `export PATH="$HOME/.local/bin:$PATH"`)
25
+ - git identity configured globally — required, tests create repos and make commits
26
+ - Activate: `source .venv/bin/activate` (or prefix commands with `uv run`)
27
+
28
+ ## Commands
29
+
30
+ | Task | Command |
31
+ |---|---|
32
+ | Fresh install | `uv venv --python 3.12 && uv pip install -e ".[dev,all]"` |
33
+ | Tests (CI parity) | `MPLBACKEND=Agg python -m pytest -m "not slow"` |
34
+ | Tests with coverage (Makefile) | `make test` (reinstalls `.[all]` first — see gotcha #1) |
35
+ | Single test | `make test-single test=tests/test_Repository/test_properties.py` |
36
+ | Lint (read-only, CI gate) | `python -m ruff check .` |
37
+ | Lint (⚠️ mutates files) | `make lint` — `--fix --unsafe-fixes`; run only when asked |
38
+ | Format | `make format` |
39
+ | Run an example | `make run-example example=commit_history` |
40
+ | MCP server (stdio) | `make mcp` → `python mcp_server/server.py` |
41
+ | Docs build | `make docs` (CI also installs `docs/requirements.txt` first) |
42
+
43
+ ## Gotchas
44
+
45
+ 1. **joblib is only in the `dev` extra.** `uv pip install -e ".[all]"` alone → 14 test failures
46
+ in `parallel_cumulative_blame` / bulk-fetch tests ("Joblib not installed"). CI installs
47
+ `.[dev]`; the Makefile's `make test` path installs `.[all]`. Install `.[dev,all]` to cover both.
48
+ 2. **`make lint` auto-fixes** and can rewrite source files. Use `python -m ruff check .` for a
49
+ read-only gate (that's what CI runs).
50
+ 3. **MCP server needs mcp v1 API** (`from mcp.server.fastmcp import FastMCP`). mcp 2.x renamed
51
+ FastMCP → MCPServer and breaks the import. Pin `mcp[cli]<2`.
52
+ 4. MCP server scans `~/Documents` by default (`Config.SCAN_ROOT_DIR`, hardcoded — not env-configurable).
53
+ 5. Redis tests are mocked (`@patch` on `gitpandas.cache`) — no Redis server is needed; the
54
+ `redis` pytest marker is informational.
55
+ 6. Tests marked `remote` fetch from github.com — network required. `slow` (example smoke tests)
56
+ is deselected by the default `-m "not slow"`.
57
+ 7. `make test-all` has a typo (`MPLBACKEmND=Agg`) — harmless, but the env var isn't set.
58
+ 8. The Makefile `gitnoc` target points at a `gitnoc/` directory that does not exist in this repo.
59
+ 9. `.gitignore` ignores `uv.lock` and `scratch`; `.venv`/`.pytest_cache`/`.ruff_cache` are kept
60
+ out of git via `.git/info/exclude` in the sandbox, not via `.gitignore`.
61
+
62
+ ## Environment variables
63
+
64
+ None required for tests, lint, or examples. `GitHubProfile` (in `gitpandas/project.py`) queries
65
+ the public GitHub REST API over the network via `requests` — no token is read by the library.
66
+
67
+ ## Codebase map
68
+
69
+ See [`codebase-map.md`](codebase-map.md).
70
+
71
+ ## Local verification
72
+
73
+ The onboarding run's evidence is in the **Local Verification Summary** below and in
74
+ [`skills/local-dev/SKILL.md`](skills/local-dev/SKILL.md).
75
+
76
+ ## Sandbox snapshot
77
+
78
+ - **Template/snapshot ID:** `b04vxdj9qvlfbrzgb205:default`
79
+ - **Captured:** 2026-09-06T20:25:59.276Z (UTC), from live session `izj5y29hgyt91gu7jjefe`
80
+ - **Contains:** everything in "Environment" above — uv 0.12.10, uv-managed CPython 3.12.14,
81
+ repo at `/home/user/work/git-pandas` with the populated `.venv`, global git identity
82
+
83
+ ## Local Verification Summary
84
+
85
+ **Run:** autobuild onboarding, 2026-09-06 (UTC), against `master` @ `e75870c` ("Preserve project revision limit remainder (#87)")
86
+ **Stack:** CPython 3.12.14 · pandas 3.0.5 · numpy 2.5.3 · git 2.47.3 · uv 0.12.10
87
+
88
+ | Check | Result |
89
+ |---|---|
90
+ | `MPLBACKEND=Agg python -m pytest -m "not slow"` | ✅ **572 passed**, 1 deselected (slow), 2 warnings, 0 failed — exit 0, ~160 s |
91
+ | `python -m ruff check .` | ✅ All checks passed |
92
+ | `make run-example example=commit_history` | ✅ exit 0 — committer stats, file-change and per-extension frames printed |
93
+ | Library API (`Repository`) | ✅ `commit_history` (5 rows), `blame(by="repository")` (5 committers), `bus_factor(by="file")` (120 rows), `bus_factor(by="repository")` (bus factor 2), `hours_estimate` (6 rows) |
94
+ | Library API (`ProjectDirectory`) | ✅ `commit_history` (3 rows, `repository` column populated) |
95
+ | MCP server | ✅ stdio JSON-RPC `initialize` + `tools/list` handshake OK (serverInfo: GitPandas MCP Server; tools incl. `list_available_repos`, `repo_branches`) |
96
+
97
+ Dev stack healthy: **yes**. No services to keep alive — the venv *is* the dev stack.
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: local-dev
3
+ description: Stand up and verify a working local dev environment for git-pandas (uv venv, pytest, ruff, examples, MCP server) — recorded from the 2026-09-06 onboarding run
4
+ ---
5
+
6
+ # Local dev onboarding — git-pandas
7
+
8
+ Durable record of the autobuild onboarding run (2026-09-06 UTC) against `master` @ `e75870c`,
9
+ in sandbox `cmp_aPYkTByo`. The snapshot for that sandbox already contains a working
10
+ environment — this skill also documents how to rebuild it from a bare checkout.
11
+
12
+ ## What "healthy" means here
13
+
14
+ git-pandas is a pure Python library. There is no app to serve, no database, no ports.
15
+ A healthy dev stack = venv with the right extras + green test suite + clean lint + a
16
+ runnable example. Evidence below.
17
+
18
+ ## Fresh setup (from a bare checkout)
19
+
20
+ ```bash
21
+ # 1. uv (package manager the repo standardizes on — Makefile + CI both use it)
22
+ curl -LsSf https://astral.sh/uv/install.sh | sh
23
+ export PATH="$HOME/.local/bin:$PATH"
24
+
25
+ # 2. git identity — tests create repos and make commits
26
+ git config --global user.email "dev@example.com"
27
+ git config --global user.name "Dev"
28
+
29
+ # 3. venv on 3.12 (matches the CI matrix; uv downloads its managed CPython)
30
+ uv venv --python 3.12
31
+
32
+ # 4. deps — dev AND all. dev is not optional: joblib + pytest-mock live only there
33
+ uv pip install -e ".[dev,all]"
34
+
35
+ # 5. optional, only if you need the MCP server entry point (make mcp)
36
+ uv pip install "mcp[cli]<2" uvicorn
37
+ ```
38
+
39
+ ## Verification checklist (all must pass)
40
+
41
+ 1. `source .venv/bin/activate`
42
+ 2. `MPLBACKEND=Agg python -m pytest -m "not slow"` → expect **572 passed, 1 deselected
43
+ (slow), 0 failed** (~160 s; tests marked `remote` fetch from github.com — network required)
44
+ 3. `python -m ruff check .` → "All checks passed!"
45
+ 4. `MPLBACKEND=Agg python examples/commit_history.py` → exit 0, prints committer stats and
46
+ file-change frames analyzing the git-pandas repo itself
47
+ 5. Optional MCP smoke: pipe an `initialize` + `tools/list` JSON-RPC pair into
48
+ `python mcp_server/server.py` and check for a `serverInfo` response
49
+
50
+ ## Known traps (cost real time during onboarding)
51
+
52
+ - **joblib only in `dev` extra.** Installing `.[all]` (what `make test` does via `setup-all`)
53
+ yields 14 failures in `parallel_cumulative_blame` + bulk-fetch tests, all logging
54
+ "Joblib not installed". Fix: install `.[dev,all]`. CI avoids this by installing `.[dev]`.
55
+ - **mcp 2.x breaks the server.** `mcp_server/server.py` imports the v1 API
56
+ (`mcp.server.fastmcp.FastMCP`); mcp 2.x renamed it to `MCPServer`. Pin `mcp[cli]<2`
57
+ (onboarding used 1.29.1). The pytest suite never hits this — `test_mcp_serialization.py`
58
+ stubs the import.
59
+ - **`make lint` mutates** (`ruff check --fix --unsafe-fixes`). The CI gate is the read-only
60
+ `python -m ruff check .` — use that unless asked to auto-fix.
61
+ - API signatures to not mis-guess (verified): `blame(rev, committer, by, ignore_globs,
62
+ include_globs)` — no `exts` kwarg; `bus_factor(by, ignore_globs, include_globs)` — no
63
+ `limit` kwarg; `ProjectDirectory(working_dir)` — no `max_depth`, and it has no
64
+ `repositories()` attribute.
65
+
66
+ ## Evidence from the onboarding run
67
+
68
+ - CPython 3.12.14 · pandas 3.0.5 · numpy 2.5.3 · git 2.47.3 · uv 0.12.10 · gitpandas 2.5.0 (editable)
69
+ - pytest `-m "not slow"`: **572 passed, 1 deselected, 2 warnings, 0 failed**, exit 0 (~160 s)
70
+ - ruff check: all checks passed
71
+ - `examples/commit_history.py`: exit 0 — repository + project-directory analyses printed
72
+ - Direct API: `commit_history`, `blame(by="repository")`, `bus_factor(by="file")` (120 rows),
73
+ `bus_factor(by="repository")` (2), `hours_estimate` (6 rows), `ProjectDirectory.commit_history`
74
+ - MCP: stdio JSON-RPC initialize + tools/list handshake OK with mcp 1.29.1
75
+ - Snapshot baked after all checks passed: template `b04vxdj9qvlfbrzgb205:default`
76
+ @ 2026-09-06T20:25:59.276Z
@@ -1,6 +1,75 @@
1
1
  Unreleased
2
2
  ==========
3
3
 
4
+ v2.7.0
5
+ ======
6
+
7
+ ## New Features
8
+
9
+ ### Author-Date Commit Timing
10
+
11
+ * **NEW**: `Repository.commit_history()`, `Repository.hours_estimate()` and the matching `ProjectDirectory` methods accept `date_source="committer"` (default) or `"author"`. With `"author"` the `date` index, the `days` cutoff and the session grouping in `hours_estimate` use the author timestamp, so rebased, cherry-picked or squash-merged history (where every rewritten commit shares one committer timestamp) no longer collapses into a single session. Any other value raises `ValueError`. Default output is unchanged. `date_source` is part of the cache key, so `RedisDFCache` entries written by earlier versions are simply not matched; flush them if you share a cache across versions.
12
+
13
+ ## Bug Fixes
14
+
15
+ ### Blame and Bus Factor Validation
16
+
17
+ * **FIXED**: Repository and project blame/bus-factor methods reject invalid `by` values with a `ValueError` naming the allowed values before accessing Git or caches.
18
+
19
+ v2.6.0
20
+ ======
21
+
22
+ ## Bug Fixes
23
+
24
+ ### Project-Wide Limit Allocation
25
+
26
+ **This is a user-visible change to the output of `ProjectDirectory.hours_estimate()`,
27
+ `commit_history()` and `file_change_history()` whenever `limit` is used: they now return
28
+ the number of rows that was asked for, which is generally more than before.**
29
+
30
+ * **FIXED**: `ProjectDirectory.hours_estimate()`, `ProjectDirectory.commit_history()` and `ProjectDirectory.file_change_history()` divided a project-wide `limit` with `int(limit / len(self.repo_dirs))` and handed the same truncated value to every repository. Three defects followed: any `limit` below the repository count floored to zero and silently returned an empty frame (`commit_history(limit=1)` over two repositories returned no rows at all), the remainder was discarded (`limit=3` returned 2 rows, `limit=5` returned 4), and the divisor was `repo_dirs`, which is not filtered by `ignore_repos` when the project is built from `Repository` instances, so an ignored repository still consumed a share of the limit. All three now use the same quotient-plus-remainder allocation `revs()` already used: the first `limit % len(repos)` repositories receive one extra commit, and the shares are taken over the `ignore_repos`-filtered `repos`.
31
+ * **FIXED**: Those three methods raised `ZeroDivisionError` on a project with no repositories, as did `ProjectDirectory.revs(num_datapoints=N)`. They now return the same empty DataFrame an all-failing project returns.
32
+
33
+ ### Project Revision Limits
34
+
35
+ * **FIXED**: `ProjectDirectory.revs(limit=N)` now distributes remainder revisions to repositories in project order instead of discarding them, so populated projects return up to the requested limit for values smaller than or not evenly divisible by the repository count.
36
+
37
+ ### Punchcard Normalization
38
+
39
+ * **FIXED**: `Repository.punchcard()` and `ProjectDirectory.punchcard()` now preserve finite zeros when normalizing an all-zero metric instead of producing `NaN` values through division by zero.
40
+
41
+ ### Revision Sampling (`num_datapoints`)
42
+
43
+ **This is a user-visible change to the output of `revs`, `cumulative_blame` and
44
+ `parallel_cumulative_blame` whenever `num_datapoints` is used: they now return the number
45
+ of rows that was asked for, which is generally fewer than before.**
46
+
47
+ * **FIXED**: `Repository.revs(num_datapoints=N)` returned more than `N` revisions. It derived `skip = int(commit_count / N)` and then kept every `skip`th row, so integer truncation inflated the result — on a 10-commit history `num_datapoints=4` returned 5 rows and `num_datapoints=6` returned all 10. It now selects exactly `min(N, commit_count)` evenly distributed revisions, keeping the existing newest-first order and always including both the newest and the oldest revision. `Repository.cumulative_blame()` and `Repository.parallel_cumulative_blame()` delegate `num_datapoints` to `revs()`, so they no longer perform (and report) more blame work than requested.
48
+ * **CHANGED**: A non-positive `num_datapoints` now raises `ValueError` from `Repository.revs()` and `ProjectDirectory.revs()`. Previously `num_datapoints=0` raised `ZeroDivisionError` from the internal skip calculation and negative values produced an arbitrary slice.
49
+ * **FIXED**: `ProjectDirectory.revs(num_datapoints=N)` divides `N` across the member repositories, which floored to zero datapoints per repository whenever `N` was smaller than the repository count. Each repository now contributes at least one revision.
50
+ * Explicit `limit` and `skip` arguments are unaffected; `num_datapoints` still applies only when neither is supplied.
51
+
52
+ ### pandas 3 Compatibility
53
+
54
+ * **CHANGED**: The `pandas>=2.0.0,<3.0.0` requirement is now `pandas>=2.0.0`. pandas 3 has been out for a while, so the upper cap meant `pip install git-pandas` either downgraded pandas or failed to resolve in any environment that already wanted pandas 3. pandas 3 requires Python >= 3.11, so resolvers on this package's declared 3.10 floor keep selecting pandas 2.x without needing an environment marker. CI now runs the suite against both majors.
55
+ * **FIXED**: `Repository.hours_estimate()` and `ProjectDirectory.hours_estimate()` raised `TypeError: unsupported operand type(s) for *: 'datetime.datetime' and 'float'` on pandas 3. The method derived per-commit timestamps with `commits.index.values.tolist()`, which yields integer nanoseconds on pandas 2 but `datetime.datetime` objects on pandas 3. It now uses `(index - index[0]).total_seconds()`, which is independent of the index's datetime64 resolution — relevant because pandas 3 preserves the second resolution of `to_datetime(..., unit="s")` rather than always upcasting to nanoseconds. Estimated hours are unchanged on pandas 2.
56
+ * **FIXED**: `plot_lifeline()` raised `AttributeError: 'StringArray' object has no attribute 'sort'` on pandas 3. `Series.unique()` returns a NumPy array on pandas 2 but a `StringArray` under pandas 3's default `str` dtype, and only the former supports in-place `.sort()`. The unique filenames are now sorted with `sorted()`.
57
+
58
+ ### Optional Redis & Coverage Dependencies
59
+ * **CHANGED** (user-visible): `redis` and `coverage` are no longer installed by `pip install git-pandas`. Neither is used by the core library — `redis` is imported behind a guard in `gitpandas.cache` and `coverage` is imported lazily inside `Repository.coverage()` — so a plain install no longer pulls in a Redis client and a test-coverage tool. They are now optional extras: install `git-pandas[redis]` to use `RedisDFCache`, and `git-pandas[coverage]` to use `Repository.coverage()` / `Repository.file_change_rates(coverage=True)`. Both are included in the `all` and `dev` extras. **Anyone relying on `RedisDFCache` after a bare `pip install git-pandas` must now install `git-pandas[redis]`**; the failure mode is the existing explicit `ImportError("Need redis installed to use redis cache")`.
60
+ * **FIXED**: `Repository.coverage()` swallowed a missing `coverage` package. The `import coverage` sat inside a `try` whose final handler is a broad `except Exception`, so on a repository that *does* have a `.coverage` file the result was an empty DataFrame — indistinguishable from "no coverage data". The import now happens outside that block and raises an `ImportError` naming the `git-pandas[coverage]` extra. `Repository.file_change_rates(coverage=True)` re-raises it rather than returning an empty frame. Genuine "no coverage data" cases still return the empty DataFrame as before.
61
+
62
+ ### Cumulative Blame Output Shape
63
+
64
+ **This is a user-visible change to the output of `cumulative_blame` and
65
+ `parallel_cumulative_blame`. Callers that index those frames positionally (`iloc[0]`,
66
+ `columns[0]`) will see different data.**
67
+
68
+ * **FIXED**: `Repository.cumulative_blame()` and `Repository.parallel_cumulative_blame()` returned the string `repository` label column (and any `labels_to_add` columns) mixed in with the per-contributor LOC counts, so `df.sum(axis=1)` — total LOC over time — raised `TypeError: can only concatenate str (not "int") to str`. Label columns are now dropped, so the returned columns are exactly the contributor names, matching the columns `ProjectDirectory.cumulative_blame()` already returned.
69
+ * **FIXED**: All three cumulative blame methods (`Repository.cumulative_blame()`, `Repository.parallel_cumulative_blame()`, `ProjectDirectory.cumulative_blame()`) returned a reverse-chronological (monotonically decreasing) `DatetimeIndex`, because `revs()` yields newest-first and nothing sorted. That made label-based date slicing raise `KeyError: Value based partial slicing on non-monotonic DatetimeIndexes...`, silently negated `.diff()`, and made `.iloc[-1]` the oldest row. The index is now sorted ascending.
70
+ * **ENHANCED**: The zero-column filter in both `Repository` methods now does a numeric sum instead of an O(n) string concatenation over the label column.
71
+ * **CHANGED**: When several revisions share one timestamp, `ProjectDirectory.cumulative_blame()` now consistently keeps the newest of them (`index.duplicated(keep="last")` on the ascending index). Previously a single-repository project kept the newest and a multi-repository project kept the oldest, because only the latter was index-sorted by the merge.
72
+
4
73
  v2.5.0
5
74
  ======
6
75
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: git-pandas
3
- Version: 2.5.0
3
+ Version: 2.7.0
4
4
  Summary: A utility for interacting with data from git repositories as Pandas dataframes
5
5
  Project-URL: Documentation, https://gitpandas.mcginniscommawill.com
6
6
  Project-URL: Source, https://github.com/wdm0006/git-pandas
@@ -13,31 +13,37 @@ Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Requires-Python: >=3.10
16
- Requires-Dist: coverage>=5.0.0
17
16
  Requires-Dist: gitpython>=1.0.0
18
17
  Requires-Dist: importlib-metadata>=1.0; python_version < '3.8'
19
18
  Requires-Dist: numpy>=1.9.0
20
- Requires-Dist: pandas<3.0.0,>=2.0.0
21
- Requires-Dist: redis
19
+ Requires-Dist: pandas>=2.0.0
22
20
  Requires-Dist: requests
23
21
  Provides-Extra: all
22
+ Requires-Dist: coverage>=5.0.0; extra == 'all'
24
23
  Requires-Dist: lifelines; extra == 'all'
25
24
  Requires-Dist: matplotlib; extra == 'all'
26
25
  Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
27
26
  Requires-Dist: pytest>=7.0.0; extra == 'all'
27
+ Requires-Dist: redis; extra == 'all'
28
28
  Requires-Dist: ruff>=0.1.0; extra == 'all'
29
29
  Requires-Dist: sphinx>=7.0.0; extra == 'all'
30
+ Provides-Extra: coverage
31
+ Requires-Dist: coverage>=5.0.0; extra == 'coverage'
30
32
  Provides-Extra: dev
33
+ Requires-Dist: coverage>=5.0.0; extra == 'dev'
31
34
  Requires-Dist: joblib; extra == 'dev'
32
35
  Requires-Dist: matplotlib; extra == 'dev'
33
36
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
34
37
  Requires-Dist: pytest-mock; extra == 'dev'
35
38
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
39
+ Requires-Dist: redis; extra == 'dev'
36
40
  Requires-Dist: ruff>=0.1.0; extra == 'dev'
37
41
  Requires-Dist: sphinx>=7.0.0; extra == 'dev'
38
42
  Provides-Extra: examples
39
43
  Requires-Dist: lifelines; extra == 'examples'
40
44
  Requires-Dist: matplotlib; extra == 'examples'
45
+ Provides-Extra: redis
46
+ Requires-Dist: redis; extra == 'redis'
41
47
  Description-Content-Type: text/markdown
42
48
 
43
49
  Git-Pandas
@@ -49,19 +55,16 @@ Git-Pandas is a powerful Python library that transforms Git repository data into
49
55
 
50
56
  ![Cumulative Blame](https://raw.githubusercontent.com/wdm0006/git-pandas/master/examples/img/githubblame.png)
51
57
 
52
- ## What's New in v2.5.0
58
+ ## What's New in v2.7.0
53
59
 
54
- - **🗂️ File-wise Bus Factor**: Calculate bus factor at the individual file level
55
- - **🗄️ Cache Management**: Advanced cache invalidation and statistics
56
- - **🌐 Remote Operations**: Safe remote fetching and bulk operations
57
- - **⚡ Performance Guide**: Comprehensive optimization documentation
58
- - **💾 Enhanced Caching**: Disk-based caching and timestamp tracking
59
- - **🎯 Correctness**: A large batch of fixes across the caching and multi-repository
60
- aggregation layers. Several metrics — file churn, hours estimates, file ownership,
61
- bus factor, and multi-repository blame — returned wrong numbers under conditions
62
- described in the [changelog](CHANGELOG.md). Note that `commits_in_tags()` now returns
63
- one row per commit rather than one per tag, and `ProjectDirectory.blame(by="file")`
64
- gains a `repository` index level.
60
+ - **🕰️ Author-Date Timing**: `commit_history()` and `hours_estimate()` (on `Repository` and
61
+ `ProjectDirectory`) accept `date_source="author"` so rebased, cherry-picked or squash-merged
62
+ history is timed by when work was authored. The default (`"committer"`) is unchanged.
63
+ - **🛡️ Clear `by=` Errors**: `blame()` and `bus_factor()` now raise a `ValueError` naming the
64
+ allowed values instead of `UnboundLocalError` or returning `None`.
65
+
66
+ See the [changelog](CHANGELOG.md) for the full list, including the migration notes for
67
+ the `cumulative_blame` and `revs` output changes.
65
68
 
66
69
  ## Why Git-Pandas?
67
70
 
@@ -141,7 +144,10 @@ For enhanced functionality, install additional packages:
141
144
  pip install joblib
142
145
 
143
146
  # For Redis caching
144
- pip install redis
147
+ pip install "git-pandas[redis]"
148
+
149
+ # For Repository.coverage() / .coverage file parsing
150
+ pip install "git-pandas[coverage]"
145
151
 
146
152
  # For visualization
147
153
  pip install matplotlib seaborn
@@ -375,6 +381,7 @@ This project is BSD licensed (see [LICENSE.md](LICENSE.md))
375
381
 
376
382
  ## Version History
377
383
 
384
+ - **v2.6.0** (2026): pandas 3 support, optional `redis`/`coverage` extras, and correctness fixes to cumulative blame output shape, `revs(num_datapoints=...)`, punchcard normalization, and project-wide `limit` allocation
378
385
  - **v2.5.0** (2026): File-wise bus factor, cache management, remote operations, performance guide, and a large correctness batch across caching and multi-repository aggregation
379
386
  - **v2.4.0** (2024): Enhanced caching system with timestamps
380
387
  - **v2.2.1** (2023): Stability improvements and bug fixes
@@ -7,19 +7,16 @@ Git-Pandas is a powerful Python library that transforms Git repository data into
7
7
 
8
8
  ![Cumulative Blame](https://raw.githubusercontent.com/wdm0006/git-pandas/master/examples/img/githubblame.png)
9
9
 
10
- ## What's New in v2.5.0
11
-
12
- - **🗂️ File-wise Bus Factor**: Calculate bus factor at the individual file level
13
- - **🗄️ Cache Management**: Advanced cache invalidation and statistics
14
- - **🌐 Remote Operations**: Safe remote fetching and bulk operations
15
- - **⚡ Performance Guide**: Comprehensive optimization documentation
16
- - **💾 Enhanced Caching**: Disk-based caching and timestamp tracking
17
- - **🎯 Correctness**: A large batch of fixes across the caching and multi-repository
18
- aggregation layers. Several metrics — file churn, hours estimates, file ownership,
19
- bus factor, and multi-repository blame — returned wrong numbers under conditions
20
- described in the [changelog](CHANGELOG.md). Note that `commits_in_tags()` now returns
21
- one row per commit rather than one per tag, and `ProjectDirectory.blame(by="file")`
22
- gains a `repository` index level.
10
+ ## What's New in v2.7.0
11
+
12
+ - **🕰️ Author-Date Timing**: `commit_history()` and `hours_estimate()` (on `Repository` and
13
+ `ProjectDirectory`) accept `date_source="author"` so rebased, cherry-picked or squash-merged
14
+ history is timed by when work was authored. The default (`"committer"`) is unchanged.
15
+ - **🛡️ Clear `by=` Errors**: `blame()` and `bus_factor()` now raise a `ValueError` naming the
16
+ allowed values instead of `UnboundLocalError` or returning `None`.
17
+
18
+ See the [changelog](CHANGELOG.md) for the full list, including the migration notes for
19
+ the `cumulative_blame` and `revs` output changes.
23
20
 
24
21
  ## Why Git-Pandas?
25
22
 
@@ -99,7 +96,10 @@ For enhanced functionality, install additional packages:
99
96
  pip install joblib
100
97
 
101
98
  # For Redis caching
102
- pip install redis
99
+ pip install "git-pandas[redis]"
100
+
101
+ # For Repository.coverage() / .coverage file parsing
102
+ pip install "git-pandas[coverage]"
103
103
 
104
104
  # For visualization
105
105
  pip install matplotlib seaborn
@@ -333,6 +333,7 @@ This project is BSD licensed (see [LICENSE.md](LICENSE.md))
333
333
 
334
334
  ## Version History
335
335
 
336
+ - **v2.6.0** (2026): pandas 3 support, optional `redis`/`coverage` extras, and correctness fixes to cumulative blame output shape, `revs(num_datapoints=...)`, punchcard normalization, and project-wide `limit` allocation
336
337
  - **v2.5.0** (2026): File-wise bus factor, cache management, remote operations, performance guide, and a large correctness batch across caching and multi-repository aggregation
337
338
  - **v2.4.0** (2024): Enhanced caching system with timestamps
338
339
  - **v2.2.1** (2023): Stability improvements and bug fixes
@@ -56,7 +56,8 @@ For persistent caching that survives between sessions:
56
56
  Redis Cache (RedisDFCache)
57
57
  ~~~~~~~~~~~~~~~~~~~~~~~~~~
58
58
 
59
- For persistent caching across sessions, use Redis:
59
+ For persistent caching across sessions, use Redis. The Redis client is an optional
60
+ dependency, installed with ``pip install "git-pandas[redis]"``:
60
61
 
61
62
  .. code-block:: python
62
63
 
@@ -21,6 +21,9 @@ Install Git-Pandas using pip:
21
21
 
22
22
  pip install git-pandas
23
23
 
24
+ Optional features live in extras: ``pip install "git-pandas[redis]"`` for the Redis cache
25
+ backend and ``pip install "git-pandas[coverage]"`` for ``Repository.coverage()``.
26
+
24
27
  Basic Usage
25
28
  ~~~~~~~~~~~
26
29
 
@@ -96,12 +96,16 @@ Track code ownership over time:
96
96
 
97
97
  .. code-block:: python
98
98
 
99
- # Get cumulative blame
99
+ # Get cumulative blame: an ascending DatetimeIndex, one column per contributor
100
100
  blame_df = repo.cumulative_blame()
101
-
101
+
102
+ # Total LOC over time, and growth per revision
103
+ blame_df.sum(axis=1)
104
+ blame_df.sum(axis=1).diff()
105
+
102
106
  # Plot cumulative blame using pandas plotting
103
107
  import matplotlib.pyplot as plt
104
- blame_df.plot(x='date', y='loc', title='Cumulative Blame Over Time')
108
+ blame_df.plot(kind='area', stacked=True, title='Cumulative Blame Over Time')
105
109
  plt.show()
106
110
 
107
111
  Bus Factor Analysis