gitoxide-py 0.87.0__tar.gz → 0.87.1__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 (40) hide show
  1. gitoxide_py-0.87.1/.github/github-automations.yml +7 -0
  2. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/.github/workflows/ci.yml +28 -0
  3. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/.github/workflows/release.yml +31 -3
  4. gitoxide_py-0.87.1/.gitmodules +4 -0
  5. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/Cargo.lock +22 -1
  6. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/Cargo.toml +3 -2
  7. gitoxide_py-0.87.1/PKG-INFO +100 -0
  8. gitoxide_py-0.87.1/README.md +72 -0
  9. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/RELEASING.md +7 -0
  10. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/pyproject.toml +22 -0
  11. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/scripts/publish.py +0 -1
  12. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/src/lib.rs +4 -2
  13. gitoxide_py-0.87.1/src/lines.rs +324 -0
  14. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/src/repository.rs +115 -2
  15. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/src/types.rs +87 -0
  16. gitoxide_py-0.87.1/stubs/gitoxide_py/__init__.py +5 -0
  17. gitoxide_py-0.87.1/stubs/gitoxide_py/__init__.pyi +811 -0
  18. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/conftest.py +1 -3
  19. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_config.py +1 -2
  20. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_edge_cases.py +2 -5
  21. gitoxide_py-0.87.1/tests/test_lines.py +248 -0
  22. gitoxide_py-0.87.1/tests/test_lines_differential.py +446 -0
  23. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_objects.py +1 -3
  24. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_refs_write.py +1 -2
  25. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_repository.py +1 -2
  26. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_revision.py +4 -12
  27. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_worktree_state.py +1 -2
  28. gitoxide_py-0.87.0/PKG-INFO +0 -124
  29. gitoxide_py-0.87.0/README.md +0 -96
  30. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/.github/windows-check/verify-wheel.sh +0 -0
  31. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/.gitignore +0 -0
  32. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/LICENSE +0 -0
  33. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/build.rs +0 -0
  34. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/scripts/check_version_tracks_gix.py +0 -0
  35. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/src/util.rs +0 -0
  36. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_gil_release.py +0 -0
  37. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_refs.py +0 -0
  38. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_repr.py +0 -0
  39. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/tests/test_submodules.py +0 -0
  40. {gitoxide_py-0.87.0 → gitoxide_py-0.87.1}/uv.lock +0 -0
@@ -0,0 +1,7 @@
1
+ version: 1
2
+
3
+ automerge:
4
+ label: automerge
5
+ base_branches: [main]
6
+ merge_method: squash
7
+ delete_branch: true
@@ -77,6 +77,34 @@ jobs:
77
77
  - name: pytest
78
78
  run: uv run --no-sync pytest -v
79
79
 
80
+ # The checked-in stub snapshot lets Griffe compare the Python API across
81
+ # refs without rebuilding the base revision. Keep only structural
82
+ # findings: default-expression changes are intentionally noisy here.
83
+ - name: Check Python API surface
84
+ if: github.event_name == 'pull_request'
85
+ run: |
86
+ base_sha="${{ github.event.pull_request.base.sha }}"
87
+ if ! git cat-file -e "$base_sha:stubs/gitoxide_py/__init__.pyi"; then
88
+ echo "Griffe API check skipped: the base ref predates the tracked stub."
89
+ exit 0
90
+ fi
91
+
92
+ set -o pipefail
93
+ if ! output="$(uv tool run --from griffe griffe check gitoxide_py \
94
+ --search stubs \
95
+ --find-stubs-packages \
96
+ --against "$base_sha" \
97
+ --base-ref "${{ github.event.pull_request.head.sha }}" \
98
+ --format github 2>&1)"; then
99
+ echo "::warning::Griffe API check could not load the API snapshot"
100
+ echo "$output"
101
+ exit 0
102
+ fi
103
+ echo "$output" | grep -E \
104
+ 'Public object was removed|Parameter was removed|Parameter was added as required|Positional parameter was moved|Public object points to a different kind of object' \
105
+ || true
106
+
107
+
80
108
  # `cargo llvm-cov show-env` prints the RUSTC_WRAPPER/LLVM_PROFILE_FILE/etc
81
109
  # that make a `cargo build` instrumented and its profile data land where
82
110
  # `cargo llvm-cov report` expects it. The Rust code here is only ever
@@ -1,8 +1,8 @@
1
1
  name: Release
2
2
 
3
3
  # Distributions are built for every push to main so a broken build surfaces
4
- # then, not at tag time. Publishing happens only for a tag, which is also
5
- # where the version comes from.
4
+ # then, not at tag time. Publishing to PyPI happens only for a tag, which is
5
+ # also where the version comes from; main builds go to the tailnet dev index.
6
6
  on:
7
7
  push:
8
8
  branches: [main]
@@ -253,11 +253,39 @@ jobs:
253
253
  - name: Upload
254
254
  run: uv run --no-project python scripts/publish.py https://pypi.org /dist
255
255
 
256
+ # Every main build's wheels go to the tailnet devpi's volatile dev index, so
257
+ # consumers can pin an unreleased main before a tag. The index prunes old
258
+ # builds; pin a PyPI release before merging. Same index and account as the
259
+ # Ninot dev wheels.
260
+ publish-dev:
261
+ name: Publish to the tailnet dev index
262
+ needs: [build, windows-check]
263
+ if: github.ref == 'refs/heads/main'
264
+ runs-on: [self-hosted, feguitv]
265
+ timeout-minutes: 20
266
+ container:
267
+ image: localhost:5000/python-ci:latest
268
+ volumes:
269
+ - /var/tmp/gitoxide-py-release/${{ github.run_id }}:/dist
270
+ defaults:
271
+ run:
272
+ shell: bash
273
+ steps:
274
+ - name: Upload
275
+ env:
276
+ DEVPI_UPLOAD_PASSWORD: ${{ secrets.DEVPI_UPLOAD_PASSWORD }}
277
+ INDEX: https://pypi.tailee1e3e.ts.net/infra/ninot-dev
278
+ run: |
279
+ test -n "${DEVPI_UPLOAD_PASSWORD:?DEVPI_UPLOAD_PASSWORD is required}"
280
+ # --check-url skips files the index already has, so a rerun passes.
281
+ uv publish --check-url "$INDEX/+simple/" --publish-url "$INDEX/" \
282
+ --username infra --password "$DEVPI_UPLOAD_PASSWORD" /dist/*.whl
283
+
256
284
  # The shared directory lives on the runner, outside any workspace, so
257
285
  # nothing else reclaims it.
258
286
  cleanup:
259
287
  name: Clean up
260
- needs: [build, windows-check, publish, publish-testpypi]
288
+ needs: [build, windows-check, publish, publish-testpypi, publish-dev]
261
289
  if: always()
262
290
  runs-on: [self-hosted, feguitv]
263
291
  timeout-minutes: 10
@@ -0,0 +1,4 @@
1
+ [submodule "tools/rulesets"]
2
+ path = tools/rulesets
3
+ url = git@github.com:feliupol/rulesets.git
4
+ branch = main
@@ -291,7 +291,7 @@ dependencies = [
291
291
 
292
292
  [[package]]
293
293
  name = "gitoxide-py"
294
- version = "0.87.0"
294
+ version = "0.87.1"
295
295
  dependencies = [
296
296
  "gix",
297
297
  "pyo3",
@@ -305,6 +305,7 @@ checksum = "fdefc1465d8631807deaf504dacdeff628b978de515653b47976ca7c28ab2a7a"
305
305
  dependencies = [
306
306
  "gix-actor",
307
307
  "gix-attributes",
308
+ "gix-blame",
308
309
  "gix-command",
309
310
  "gix-commitgraph",
310
311
  "gix-config",
@@ -389,6 +390,26 @@ dependencies = [
389
390
  "gix-error",
390
391
  ]
391
392
 
393
+ [[package]]
394
+ name = "gix-blame"
395
+ version = "0.17.1"
396
+ source = "registry+https://github.com/rust-lang/crates.io-index"
397
+ checksum = "54fe27f8037c58cb4b6d60fe0aacf7ee2c998633cedf4e989351c07d9a3a575d"
398
+ dependencies = [
399
+ "gix-commitgraph",
400
+ "gix-date",
401
+ "gix-diff",
402
+ "gix-error",
403
+ "gix-hash",
404
+ "gix-object",
405
+ "gix-revwalk",
406
+ "gix-trace",
407
+ "gix-traverse",
408
+ "gix-worktree",
409
+ "smallvec",
410
+ "thiserror",
411
+ ]
412
+
392
413
  [[package]]
393
414
  name = "gix-chunk"
394
415
  version = "0.8.0"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "gitoxide-py"
3
- version = "0.87.0"
3
+ version = "0.87.1"
4
4
  edition = "2021"
5
5
  description = "Typed Python bindings to the gix (gitoxide) engine that release the GIL for every call"
6
6
  license = "MIT"
@@ -17,7 +17,7 @@ crate-type = ["cdylib"]
17
17
  # rev-parse / commit traversal; `sha1` is the portable (non-C) SHA1 backend.
18
18
  # `status` is the umbrella feature for the working-tree-aware half of gix: it
19
19
  # pulls in `index`, `blob-diff`, `dirwalk`, `excludes` and `attributes`, all of
20
- # which are pure Rust too.
20
+ # which are pure Rust too. `blame` adds `gix-blame` for `Repository.blame_file`.
21
21
  # None of this makes `gix::Repository` itself `Sync` — see the `Repository`
22
22
  # pyclass doc comment in src/lib.rs for why it's backed by
23
23
  # `gix::ThreadSafeRepository` instead.
@@ -26,6 +26,7 @@ gix = { version = "0.87", default-features = false, features = [
26
26
  "revision",
27
27
  "sha1",
28
28
  "status",
29
+ "blame",
29
30
  ] }
30
31
 
31
32
  [dependencies.pyo3]
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitoxide-py
3
+ Version: 0.87.1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.9
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Python :: Implementation :: CPython
14
+ Classifier: Programming Language :: Rust
15
+ Classifier: Topic :: Software Development :: Version Control :: Git
16
+ Classifier: Typing :: Typed
17
+ License-File: LICENSE
18
+ Summary: Typed Python bindings to the gix (gitoxide) engine that release the GIL for every call
19
+ Keywords: git,gitoxide,gix,vcs,version-control,pyo3
20
+ Author: Pol Feliu
21
+ License-Expression: MIT
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
24
+ Project-URL: Homepage, https://github.com/feliupol/gitoxide-py
25
+ Project-URL: Repository, https://github.com/feliupol/gitoxide-py
26
+ Project-URL: gix (Rust crate), https://github.com/GitoxideLabs/gitoxide
27
+
28
+ # gitoxide-py
29
+
30
+ <p align="center">
31
+ <strong>Fast, typed Python access to local Git repositories.</strong>
32
+ </p>
33
+
34
+ <p align="center">
35
+ <a href="https://github.com/feliupol/gitoxide-py/actions/workflows/ci.yml">
36
+ <img src="https://github.com/feliupol/gitoxide-py/actions/workflows/ci.yml/badge.svg?branch=main" alt="Build status">
37
+ </a>
38
+ <a href="https://www.python.org/">
39
+ <img src="https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&amp;logoColor=white" alt="Python 3.9 or later">
40
+ </a>
41
+ </p>
42
+
43
+ Explore repositories, inspect history and working-tree state, and manage refs
44
+ from Python without blocking other threads.
45
+
46
+ ## Quick start
47
+
48
+ Build from a checkout of this repository:
49
+
50
+ ```bash
51
+ uv sync
52
+ uv run maturin develop --release --generate-stubs
53
+ ```
54
+
55
+ ```python
56
+ import gitoxide_py as gix
57
+
58
+ repo = gix.discover(".")
59
+
60
+ print(repo.git_dir)
61
+ print(repo.head_name)
62
+ print(repo.head_commit().summary)
63
+
64
+ for commit in repo.commits(max_count=10):
65
+ print(commit.short_id, commit.author.name, commit.summary)
66
+
67
+ print(repo.branches())
68
+ print(repo.tags())
69
+ print(repo.read_blob("HEAD:README.md").decode())
70
+ ```
71
+
72
+ The generated API snapshot used by the Griffe CI check lives in
73
+ `stubs/gitoxide_py/__init__.pyi`. Regenerate it after changing the PyO3 API by
74
+ building a wheel with `uv run maturin build --generate-stubs`, then replace the
75
+ tracked stub with the wheel's `gitoxide_py/__init__.pyi`.
76
+
77
+ ## What it covers
78
+
79
+ - **Repository and history:** HEAD and branch state, revisions, commit walks,
80
+ merge bases, ahead/behind counts, ancestry, references, branches, tags, and
81
+ reflogs.
82
+ - **Content inspection:** object kinds, tree entries, blob reads,
83
+ tree-to-tree diffs with line hunks, and blame.
84
+ - **Working repository state:** remotes, linked worktrees, submodules, local
85
+ configuration, index entries and conflicts, and working-tree status.
86
+ - **Focused changes:** create or delete branches and tags, and update local
87
+ configuration. These operations do not modify the working tree.
88
+ - **Python-friendly:** every Git operation releases the GIL, and generated
89
+ type information is available to mypy and pyright.
90
+
91
+ Network operations (`clone`, `fetch`, and `push`) and changes to commits,
92
+ index entries, or working-tree files are not part of the API.
93
+
94
+ ## Development
95
+
96
+ ```bash
97
+ uv run pytest -v
98
+ uv run maturin build --generate-stubs
99
+ ```
100
+
@@ -0,0 +1,72 @@
1
+ # gitoxide-py
2
+
3
+ <p align="center">
4
+ <strong>Fast, typed Python access to local Git repositories.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/feliupol/gitoxide-py/actions/workflows/ci.yml">
9
+ <img src="https://github.com/feliupol/gitoxide-py/actions/workflows/ci.yml/badge.svg?branch=main" alt="Build status">
10
+ </a>
11
+ <a href="https://www.python.org/">
12
+ <img src="https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&amp;logoColor=white" alt="Python 3.9 or later">
13
+ </a>
14
+ </p>
15
+
16
+ Explore repositories, inspect history and working-tree state, and manage refs
17
+ from Python without blocking other threads.
18
+
19
+ ## Quick start
20
+
21
+ Build from a checkout of this repository:
22
+
23
+ ```bash
24
+ uv sync
25
+ uv run maturin develop --release --generate-stubs
26
+ ```
27
+
28
+ ```python
29
+ import gitoxide_py as gix
30
+
31
+ repo = gix.discover(".")
32
+
33
+ print(repo.git_dir)
34
+ print(repo.head_name)
35
+ print(repo.head_commit().summary)
36
+
37
+ for commit in repo.commits(max_count=10):
38
+ print(commit.short_id, commit.author.name, commit.summary)
39
+
40
+ print(repo.branches())
41
+ print(repo.tags())
42
+ print(repo.read_blob("HEAD:README.md").decode())
43
+ ```
44
+
45
+ The generated API snapshot used by the Griffe CI check lives in
46
+ `stubs/gitoxide_py/__init__.pyi`. Regenerate it after changing the PyO3 API by
47
+ building a wheel with `uv run maturin build --generate-stubs`, then replace the
48
+ tracked stub with the wheel's `gitoxide_py/__init__.pyi`.
49
+
50
+ ## What it covers
51
+
52
+ - **Repository and history:** HEAD and branch state, revisions, commit walks,
53
+ merge bases, ahead/behind counts, ancestry, references, branches, tags, and
54
+ reflogs.
55
+ - **Content inspection:** object kinds, tree entries, blob reads,
56
+ tree-to-tree diffs with line hunks, and blame.
57
+ - **Working repository state:** remotes, linked worktrees, submodules, local
58
+ configuration, index entries and conflicts, and working-tree status.
59
+ - **Focused changes:** create or delete branches and tags, and update local
60
+ configuration. These operations do not modify the working tree.
61
+ - **Python-friendly:** every Git operation releases the GIL, and generated
62
+ type information is available to mypy and pyright.
63
+
64
+ Network operations (`clone`, `fetch`, and `push`) and changes to commits,
65
+ index entries, or working-tree files are not part of the API.
66
+
67
+ ## Development
68
+
69
+ ```bash
70
+ uv run pytest -v
71
+ uv run maturin build --generate-stubs
72
+ ```
@@ -2,6 +2,13 @@
2
2
 
3
3
  Wheels build on every push to `main`; a tag is what publishes them.
4
4
 
5
+ Each `main` build also uploads its wheels (`0.87.0.post4+ef3c505`-style
6
+ versions) to the tailnet devpi's volatile dev index,
7
+ `https://pypi.tailee1e3e.ts.net/infra/ninot-dev/+simple/`, for consumers that
8
+ need an unreleased change. That index prunes old builds, so pin a PyPI release
9
+ before merging. The upload needs the `DEVPI_UPLOAD_PASSWORD` Actions secret
10
+ (the devpi `infra` password).
11
+
5
12
  ## One-time setup
6
13
 
7
14
  PyPI publishes through [Trusted Publishing](https://docs.pypi.org/trusted-publishers/),
@@ -38,3 +38,25 @@ dev = ["maturin>=1.7,<2.0", "pytest>=7.0"]
38
38
 
39
39
  [tool.maturin]
40
40
  features = ["pyo3/extension-module"]
41
+
42
+ [tool.ruff]
43
+ extend = "tools/rulesets/ruff/ruff.toml"
44
+ extend-exclude = ["tools/rulesets"]
45
+ # The shared base sets no target-version; the package supports Python 3.9.
46
+ target-version = "py39"
47
+
48
+ [tool.ruff.lint]
49
+ ignore = [
50
+ # TODO(rulesets): add `match=` to the 12 broad `pytest.raises(ValueError)` calls.
51
+ "pytest-raises-too-broad",
52
+ # TODO(rulesets): add `from __future__ import annotations` to tests/conftest.py.
53
+ "future-required-type-annotation",
54
+ # TODO(rulesets): pass `check=` to subprocess.run in tests/conftest.py.
55
+ "subprocess-run-without-check",
56
+ # TODO(rulesets): use or drop the unused fixture argument in tests/test_repository.py.
57
+ "unused-function-argument",
58
+ ]
59
+
60
+ [tool.ruff.lint.per-file-ignores]
61
+ # The binding mirrors the builtin name with a module-level `open`.
62
+ "stubs/gitoxide_py/__init__.pyi" = ["builtin-variable-shadowing"]
@@ -24,7 +24,6 @@ import urllib.error
24
24
  import urllib.request
25
25
  from pathlib import Path
26
26
 
27
-
28
27
  # PyPI serves uploads from a different host than its web and OIDC endpoints;
29
28
  # TestPyPI serves both from one. Deriving the upload URL from the index base is
30
29
  # therefore correct for TestPyPI and wrong for PyPI, which is exactly the kind
@@ -1,5 +1,6 @@
1
1
  use pyo3::prelude::*;
2
2
 
3
+ mod lines;
3
4
  mod repository;
4
5
  mod types;
5
6
  mod util;
@@ -38,8 +39,9 @@ mod gitoxide_py {
38
39
 
39
40
  #[pymodule_export]
40
41
  use crate::types::{
41
- Branch, Commit, FileChange, IndexConflict, IndexEntry, Reference, ReflogEntry, Remote,
42
- Signature, StatusEntry, Submodule, SubmoduleStatus, TreeEntry, Worktree,
42
+ BlameHunk, Branch, Commit, FileChange, FileDiff, IndexConflict, IndexEntry, LineHunk,
43
+ Reference, ReflogEntry, Remote, Signature, StatusEntry, Submodule, SubmoduleStatus,
44
+ TreeEntry, Worktree,
43
45
  };
44
46
 
45
47
  use pyo3::prelude::*;