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.
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/test-suite.yml +32 -7
- git_pandas-2.7.0/.obvious/codebase-map.md +18 -0
- git_pandas-2.7.0/.obvious/config.yml +6 -0
- git_pandas-2.7.0/.obvious/obvious.md +97 -0
- git_pandas-2.7.0/.obvious/skills/local-dev/SKILL.md +76 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/CHANGELOG.md +69 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/PKG-INFO +25 -18
- {git_pandas-2.5.0 → git_pandas-2.7.0}/README.md +15 -14
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/cache.rst +2 -1
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/index.rst +3 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/usecases.rst +7 -3
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/project.py +81 -24
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/repository.py +152 -35
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/plotting.py +4 -3
- {git_pandas-2.5.0 → git_pandas-2.7.0}/pyproject.toml +13 -5
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_advanced.py +1 -0
- git_pandas-2.7.0/tests/test_Project/test_cumulative_blame_output_shape.py +127 -0
- git_pandas-2.7.0/tests/test_Project/test_date_source.py +75 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_edge_cases.py +1 -1
- git_pandas-2.7.0/tests/test_Project/test_project_limit_allocation.py +278 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_punchcard.py +17 -0
- git_pandas-2.7.0/tests/test_Project/test_revs_num_datapoints.py +99 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_cumulative_blame_duplicate_dates.py +8 -1
- git_pandas-2.7.0/tests/test_Repository/test_cumulative_blame_output_shape.py +126 -0
- git_pandas-2.7.0/tests/test_Repository/test_date_source.py +130 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_edge_cases.py +1 -1
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_error_handling.py +43 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_punchcard.py +19 -20
- git_pandas-2.7.0/tests/test_Repository/test_revs_num_datapoints.py +170 -0
- git_pandas-2.7.0/tests/test_by_validation.py +79 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_management.py +1 -1
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cached_signatures.py +2 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/project_overview.mdc +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/pytest_standards.mdc +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/python_standards.mdc +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/sphinx_docs_standards.mdc +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.cursor/rules/testing.mdc +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/ISSUE_TEMPLATE.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/docs.yml +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/pypi-publish.yml +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.github/workflows/test-docs-build.yml +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/.gitignore +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/CONTRIBUTING.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/LICENSE.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/Makefile +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/AUDIT_REPORT.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/Makefile +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/make.bat +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/requirements.txt +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/CNAME +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/_static/custom.css +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/conf.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/contributors.rst +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/performance.rst +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/project.rst +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/remote_operations.rst +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/docs/source/repository.rst +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/attributes.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/bus_analysis.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cache_management.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cache_timestamps.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cloud_repo.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/commit_history.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/cumulative_blame.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/definitions.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/file_change_rates.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/hours_estimate.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/cumulative_blame.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/cumulative_project_blame.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/githubblame.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/lifeline.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/img/stravalib_cumulative_blame.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/lifeline.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/parallel_blame.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/project_blame.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/punchcard.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/release_analytics.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/remote_fetch_and_cache_warming.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/examples/repo_file_detail.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/cache.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/logging.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/gitpandas/utilities/check_api.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/img/cumulative_blame.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/img/lifeline.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/img/punchcard.png +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/mcp_server/README.md +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/mcp_server/server.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/conftest.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_blame_file_detail_empty.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_blame_multi_repo.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_bus_factor.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_commits_in_tags.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_default_branch_detection.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_error_handling.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_file_detail_author.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_github_profile.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Project/test_properties.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/__init__.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_advanced.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_bus_factor.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_commits_in_tags.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_change_history_values.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_detail.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_operations.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_file_owner_author.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_hours_estimate.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_properties.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_Repository/test_rev_to_rev.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_bulk_fetch_warm.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_immutability.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_integration.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_key_consistency.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_threading.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_cache_warming.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_examples.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_logging.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_mcp_serialization.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_remote_fetch.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_repository_cache.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_utilities/test_check_api.py +0 -0
- {git_pandas-2.5.0 → git_pandas-2.7.0}/tests/test_utilities/test_init.py +0 -0
- {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
|
-
|
|
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
|
|
54
|
-
|
|
78
|
+
MPLBACKEND=Agg python -m pytest -v -m "not slow"
|
|
79
|
+
|
|
55
80
|
- name: Run linting
|
|
56
81
|
run: |
|
|
57
|
-
|
|
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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: git-pandas
|
|
3
|
-
Version: 2.
|
|
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
|
|
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
|

|
|
51
57
|
|
|
52
|
-
## What's New in v2.
|
|
58
|
+
## What's New in v2.7.0
|
|
53
59
|
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|

|
|
9
9
|
|
|
10
|
-
## What's New in v2.
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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(
|
|
108
|
+
blame_df.plot(kind='area', stacked=True, title='Cumulative Blame Over Time')
|
|
105
109
|
plt.show()
|
|
106
110
|
|
|
107
111
|
Bus Factor Analysis
|