rusty-git 0.3.0__cp312-abi3-win_amd64.whl

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.
rusty_git/__init__.py ADDED
@@ -0,0 +1,76 @@
1
+ """High-speed pure Rust Git engine for the ``dev-m`` suite.
2
+
3
+ This package is a thin Python shell around the compiled PyO3 extension
4
+ ``rusty_git._rusty_git``. Everything public is re-exported here so callers
5
+ never need to know about the private module name::
6
+
7
+ import rusty_git
8
+
9
+ info = await rusty_git.get_commit("/srv/repos/app.git", "HEAD")
10
+
11
+ Layering
12
+ --------
13
+ * ``inspect`` — read paths (commits, refs, blobs, diffs) via ``gix``.
14
+ * ``merge`` — 3-way merge / conflict detection via ``git2`` (libgit2).
15
+ * ``protocol`` — in-process Smart-HTTP packet-line framing.
16
+ * ``upload`` — ``git-upload-pack``: advertisement, negotiation, pack stream.
17
+ * ``receive`` — ``git-receive-pack``: pack ingest and ref updates.
18
+
19
+ The ``CommitInfo``, ``DiffHunk``, ``MergeResult``, ``UploadResult`` and
20
+ ``PushResult`` names are ``TypedDict`` declarations in ``_rusty_git.pyi`` only —
21
+ they describe the shape of the dicts the engine returns, and have no runtime
22
+ presence, so they are not re-exported here. Use them under ``TYPE_CHECKING``
23
+ for annotations.
24
+
25
+ Concurrency
26
+ -----------
27
+ Every callable is ``async``. The blocking Git work runs on a Tokio worker
28
+ thread with the GIL released, so the event loop (Granian/Tokio or
29
+ Uvicorn/uvloop) is never stalled.
30
+ """
31
+
32
+ from ._rusty_git import (
33
+ GitError,
34
+ GitObjectError,
35
+ GitProtocolError,
36
+ GitRepositoryError,
37
+ __runtime__,
38
+ __rust_version__,
39
+ __version__,
40
+ advertise,
41
+ capabilities,
42
+ compare_refs,
43
+ create_commit,
44
+ encode_pkt_line,
45
+ execute_merge,
46
+ get_commit,
47
+ get_tree_blob,
48
+ init_bare_repository,
49
+ list_branches,
50
+ receive_pack,
51
+ sync,
52
+ upload_pack,
53
+ )
54
+
55
+ __all__ = [
56
+ "GitError",
57
+ "GitObjectError",
58
+ "GitProtocolError",
59
+ "GitRepositoryError",
60
+ "__runtime__",
61
+ "__rust_version__",
62
+ "__version__",
63
+ "advertise",
64
+ "capabilities",
65
+ "compare_refs",
66
+ "create_commit",
67
+ "encode_pkt_line",
68
+ "execute_merge",
69
+ "get_commit",
70
+ "get_tree_blob",
71
+ "init_bare_repository",
72
+ "list_branches",
73
+ "receive_pack",
74
+ "sync",
75
+ "upload_pack",
76
+ ]
Binary file
@@ -0,0 +1,195 @@
1
+ """Type stubs for the compiled ``rusty_git._rusty_git`` extension module.
2
+
3
+ These are the source of truth for the async surface. Every callable is a
4
+ coroutine: the underlying blocking Git work happens on a Tokio worker thread
5
+ with the GIL released. The public package ``rusty_git`` re-exports all of this,
6
+ so application code can import from ``rusty_git`` directly.
7
+ """
8
+
9
+ from typing import TypedDict
10
+
11
+ __version__: str
12
+ __rust_version__: str
13
+ __runtime__: str
14
+
15
+ class GitError(Exception):
16
+ """Base class for every rusty-git failure."""
17
+
18
+ class GitRepositoryError(GitError):
19
+ """Repository could not be opened, created, or mutated."""
20
+
21
+ class GitObjectError(GitError):
22
+ """A commit, tree, blob, or diff could not be resolved."""
23
+
24
+ class GitProtocolError(GitError):
25
+ """Smart-HTTP packet-line negotiation failed."""
26
+
27
+ class CommitInfo(TypedDict):
28
+ sha: str
29
+ author_name: str
30
+ author_email: str
31
+ committer_name: str
32
+ committer_email: str
33
+ message: str
34
+ timestamp: str
35
+ timestamp_epoch: int
36
+ timezone_offset: int
37
+ committer_timestamp: str
38
+ committer_timestamp_epoch: int
39
+ committer_timezone_offset: int
40
+ parent_shas: list[str]
41
+
42
+ class DiffHunk(TypedDict):
43
+ old_path: str
44
+ new_path: str
45
+ old_mode: int
46
+ new_mode: int
47
+ old_start: int
48
+ old_lines: int
49
+ new_start: int
50
+ new_lines: int
51
+ content: str
52
+ is_binary: bool
53
+
54
+ class MergeResult(TypedDict):
55
+ status: str
56
+ commit_sha: str | None
57
+ conflicts: list[str] | None
58
+
59
+ class UploadResult(TypedDict):
60
+ body: bytes
61
+ object_count: int
62
+ used_have: str | None
63
+ side_band: bool
64
+
65
+ class PushResult(TypedDict):
66
+ indexed_objects: int
67
+ updated_refs: list[str]
68
+
69
+ class CommitCreateResult(TypedDict):
70
+ commit_sha: str
71
+ tree_sha: str
72
+ parent_shas: list[str]
73
+ branch: str
74
+
75
+ async def init_bare_repository(path: str) -> str:
76
+ """Create a bare repository at *path*; returns the normalised path."""
77
+
78
+ async def get_commit(repo_path: str, sha: str) -> CommitInfo:
79
+ """Resolve *sha* (or any revision) to full commit metadata."""
80
+
81
+ async def list_branches(repo_path: str) -> list[str]:
82
+ """Return the sorted local branch names."""
83
+
84
+ async def get_tree_blob(repo_path: str, ref_name: str, file_path: str) -> bytes | None:
85
+ """Return raw file bytes, or ``None`` when the path does not exist."""
86
+
87
+ async def compare_refs(repo_path: str, base_ref: str, head_ref: str) -> list[DiffHunk]:
88
+ """Return unified-diff hunks between two refs."""
89
+
90
+ async def create_commit(
91
+ repo_path: str,
92
+ branch: str,
93
+ files: dict[str, bytes | None],
94
+ message: str,
95
+ author_name: str | None = None,
96
+ author_email: str | None = None,
97
+ expected_sha: str | None = None,
98
+ force: bool = False,
99
+ ) -> CommitCreateResult:
100
+ """Create a commit atomically on *branch* with the provided *files* mapping.
101
+
102
+ *files* maps relative paths to `bytes` (create/update) or `None` (delete).
103
+ Supports initial root commits, consequent commits, and optimistic concurrency
104
+ control (`expected_sha` / force-with-lease).
105
+ """
106
+
107
+ async def execute_merge(
108
+ repo_path: str,
109
+ base_ref: str,
110
+ head_ref: str,
111
+ author_name: str | None = None,
112
+ author_email: str | None = None,
113
+ commit_message: str | None = None,
114
+ ) -> MergeResult:
115
+ """3-way merge *head_ref* into *base_ref*."""
116
+
117
+ def capabilities() -> list[str]:
118
+ """Return the Git protocols this engine can serve in-process."""
119
+
120
+ async def advertise(repo_path: str, service: str) -> bytes:
121
+ """Return the smart-HTTP ref advertisement for *service*.
122
+
123
+ *service* must be ``"git-upload-pack"`` or ``"git-receive-pack"``. The
124
+ result is the complete HTTP response body, including the
125
+ ``# service=...`` prefix line.
126
+ """
127
+
128
+ async def upload_pack(repo_path: str, body: bytes) -> UploadResult:
129
+ """Serve a ``git-upload-pack`` request body.
130
+
131
+ Returns the response body (``NAK``/``ACK`` plus the pkt-line-framed pack),
132
+ ready to send verbatim as an HTTP response.
133
+ """
134
+
135
+ async def receive_pack(
136
+ repo_path: str,
137
+ body: bytes,
138
+ author_name: str | None = None,
139
+ author_email: str | None = None,
140
+ ) -> PushResult:
141
+ """Serve a ``git-receive-pack`` request body.
142
+
143
+ Ingests the inbound pack, then moves refs. Objects are made durable before
144
+ any ref names them, so a failure never leaves a repository that ``git fsck``
145
+ rejects.
146
+ """
147
+
148
+ def encode_pkt_line(payload: bytes) -> bytes:
149
+ """Frame *payload* as a Git pkt-line.
150
+
151
+ Raises :class:`GitProtocolError` when the payload exceeds
152
+ :data:`MAX_PKT_PAYLOAD` and cannot fit in a single frame.
153
+ """
154
+
155
+ class _SyncModule:
156
+ """Synchronous interface releasing the Python GIL directly on calling thread."""
157
+
158
+ def init_bare_repository(self, path: str) -> str: ...
159
+ def get_commit(self, repo_path: str, sha: str) -> CommitInfo: ...
160
+ def list_branches(self, repo_path: str) -> list[str]: ...
161
+ def get_tree_blob(self, repo_path: str, ref_name: str, file_path: str) -> bytes | None: ...
162
+ def compare_refs(self, repo_path: str, base_ref: str, head_ref: str) -> list[DiffHunk]: ...
163
+ def create_commit(
164
+ self,
165
+ repo_path: str,
166
+ branch: str,
167
+ files: dict[str, bytes | None],
168
+ message: str,
169
+ author_name: str | None = None,
170
+ author_email: str | None = None,
171
+ expected_sha: str | None = None,
172
+ force: bool = False,
173
+ ) -> CommitCreateResult: ...
174
+ def execute_merge(
175
+ self,
176
+ repo_path: str,
177
+ base_ref: str,
178
+ head_ref: str,
179
+ author_name: str | None = None,
180
+ author_email: str | None = None,
181
+ commit_message: str | None = None,
182
+ ) -> MergeResult: ...
183
+ def capabilities(self) -> list[str]: ...
184
+ def encode_pkt_line(self, payload: bytes) -> bytes: ...
185
+ def advertise(self, repo_path: str, service: str) -> bytes: ...
186
+ def upload_pack(self, repo_path: str, body: bytes) -> UploadResult: ...
187
+ def receive_pack(
188
+ self,
189
+ repo_path: str,
190
+ body: bytes,
191
+ author_name: str | None = None,
192
+ author_email: str | None = None,
193
+ ) -> PushResult: ...
194
+
195
+ sync: _SyncModule
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.1
2
+ Name: rusty-git
3
+ Version: 0.3.0
4
+ Summary: High-speed pure Rust Git engine (gix + git2) for Python
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Classifier: Topic :: Software Development :: Version Control :: Git
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+
15
+ # rusty-git
16
+
17
+ [![GitLab CI](https://gitlab.com/framework-m/rusty-git/badges/main/pipeline.svg)](https://gitlab.com/framework-m/rusty-git/-/pipelines)
18
+ [![PyPI version](https://img.shields.io/pypi/v/rusty-git.svg)](https://pypi.org/project/rusty-git/)
19
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
20
+
21
+ **High-speed pure Rust Git engine (`gix` + `git2`) compiled via PyO3 with both native `async` (Tokio) and GIL-released `sync` APIs.**
22
+
23
+ `rusty-git` provides in-process Git operations with **zero OS subprocess forks** and **statically-compiled native wheels** (no dynamic `libgit2.so` dependencies required).
24
+
25
+ ---
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install rusty-git
31
+ # or with uv:
32
+ uv add rusty-git
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Quickstart: 100% In-Process Demo (Zero Subprocesses)
38
+
39
+ Copy-paste this into `example.py` to test immediately in any environment:
40
+
41
+ ```python
42
+ import asyncio
43
+ import os
44
+ import tempfile
45
+ import rusty_git
46
+
47
+
48
+ async def main():
49
+ with tempfile.TemporaryDirectory() as tmp_dir:
50
+ repo_path = os.path.join(tmp_dir, "demo.git")
51
+
52
+ # 1. Initialize a bare repository in-process (ZERO subprocesses)
53
+ await rusty_git.init_bare_repository(repo_path)
54
+
55
+ # 2. Create the initial root commit in-process
56
+ res = await rusty_git.create_commit(
57
+ repo_path,
58
+ branch="main",
59
+ files={
60
+ "README.md": b"# Hello from rusty-git!\n",
61
+ "src/main.py": b"print('Hello World')\n",
62
+ },
63
+ message="Initial commit",
64
+ author_name="Alice",
65
+ author_email="alice@example.com",
66
+ )
67
+ print("Root Commit SHA:", res["commit_sha"])
68
+
69
+ # 3. Inspect commit in-process
70
+ commit = await rusty_git.get_commit(repo_path, "HEAD")
71
+ print(f"Author: {commit['author_name']} <{commit['author_email']}>")
72
+ print(f"Message: {commit['message']}")
73
+
74
+ # 4. Read tree blob without disk checkout
75
+ blob = await rusty_git.get_tree_blob(repo_path, "HEAD", "README.md")
76
+ print("Blob Content:", blob.decode("utf-8").strip())
77
+
78
+ # 5. List branches
79
+ branches = await rusty_git.list_branches(repo_path)
80
+ print("Branches:", branches)
81
+
82
+
83
+ asyncio.run(main())
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Dual-Color Usage: Async & Sync
89
+
90
+ ### 1. Asynchronous API (Default - for Litestar, FastAPI, Granian, Uvicorn)
91
+
92
+ All blocking Git I/O is offloaded to a Tokio multi-threaded worker pool, releasing the Python GIL:
93
+
94
+ ```python
95
+ import asyncio
96
+ import rusty_git
97
+
98
+
99
+ async def main():
100
+ repo_path = "/path/to/your/repo"
101
+
102
+ # 1. Inspect commits
103
+ commit = await rusty_git.get_commit(repo_path, "HEAD")
104
+ print(commit["sha"], commit["message"], commit["author_name"])
105
+
106
+ # 2. Read tree blobs without disk checkout
107
+ readme = await rusty_git.get_tree_blob(repo_path, "main", "README.md")
108
+ print(readme.decode("utf-8"))
109
+
110
+ # 3. Fast unified diffs
111
+ hunks = await rusty_git.compare_refs(repo_path, "main~1", "main")
112
+ for hunk in hunks:
113
+ print(hunk["new_path"], hunk["content"])
114
+
115
+ # 4. 3-Way Merge
116
+ result = await rusty_git.execute_merge(
117
+ repo_path,
118
+ base_ref="main",
119
+ head_ref="feature",
120
+ author_name="Automated Merge",
121
+ author_email="bot@example.com",
122
+ )
123
+ if result["status"] == "merged":
124
+ print("Merged Commit SHA:", result["commit_sha"])
125
+
126
+
127
+ asyncio.run(main())
128
+ ```
129
+
130
+ ---
131
+
132
+ ### 2. Synchronous API (`rusty_git.sync` - for CLI tools, Celery, scripts)
133
+
134
+ Direct execution on the calling thread with the Python GIL cleanly released (`py.detach`):
135
+
136
+ ```python
137
+ import rusty_git
138
+
139
+ # Read commit synchronously
140
+ commit = rusty_git.sync.get_commit("/path/to/repo.git", "HEAD")
141
+ print(commit["sha"], commit["author_name"])
142
+
143
+ # List branches synchronously
144
+ branches = rusty_git.sync.list_branches("/path/to/repo.git")
145
+ print("Branches:", branches)
146
+
147
+ # Read file directly from commit tree
148
+ blob = rusty_git.sync.get_tree_blob("/path/to/repo.git", "HEAD", "pyproject.toml")
149
+
150
+ # Create a commit directly on any branch (new or existing)
151
+ res = rusty_git.sync.create_commit(
152
+ "/path/to/repo.git",
153
+ branch="feature-x",
154
+ files={"hello.txt": b"Hello world!\n"},
155
+ message="feat: add hello.txt",
156
+ )
157
+
158
+ # 3-Way Merge synchronously
159
+ result = rusty_git.sync.execute_merge(
160
+ "/path/to/repo.git",
161
+ "main",
162
+ "feature",
163
+ author_name="Merge Bot",
164
+ author_email="bot@example.com",
165
+ )
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Architectural Note: Forges & Branch Management
171
+
172
+ `rusty-git` is designed for high-performance server-side forges and tools operating on **bare repositories**:
173
+
174
+ * **Creating branches with commits**: Passing any branch name to `create_commit(...)` will create or advance that branch atomically.
175
+ * **Creating a branch without a commit**: Git branches are simply ref pointers. To create a branch pointing to an existing commit without a new commit, your application or CLI can write the commit SHA directly to `refs/heads/<branch_name>`.
176
+ * **Switching active branch (`HEAD`)**: In a bare repo or CLI, switching the symbolic reference is done by writing `ref: refs/heads/<branch_name>\n` to the `HEAD` file.
177
+
178
+ ### Engine Architecture: Pure `gix` vs. `git2` Wire Streaming
179
+
180
+ `rusty-git` intentionally separates user-facing operations from network pack streaming:
181
+
182
+ * **100% Pure `gix` Core (WebAssembly / Pyodide Ready)**: `commit` (atomic in-memory trees & commits), `inspect` (refs, tree blobs, histogram unified diffs), and `merge` (3-way ORT tree merge) run entirely on pure-Rust `gix`. They require zero C headers and run in browsers and Pyodide.
183
+ * **Wire Protocol Pack Streaming (`git2`)**: Ingesting thin packs (`git push`) and streaming sliding-window delta packs (`git clone`/`git fetch`) use `libgit2`'s battle-tested pack engine. Once `gix-pack` high-level streaming pack generation reaches full maturity, these remaining wire endpoints will transition to `gix`, completing the journey to 100% pure Rust.
184
+
185
+ This keeps `rusty-git` focused entirely on performance-critical operations without unnecessary file-I/O wrapper overhead.
186
+
187
+ ---
188
+
189
+ ## Smart-HTTP Protocol Wire Support
190
+
191
+ `rusty-git` can drive Git HTTP and SSH protocol negotiation completely in-process:
192
+
193
+ > **Note on Transport & Auth (SSH / TLS):** `rusty-git` operates directly on request/response byte streams rather than managing sockets. Transport encryption and authentication (SSH keys via `asyncssh`/OpenSSH, or TLS/mTLS client certs via Nginx/Caddy/HTTPX) happen cleanly at the transport layer before passing payloads to `rusty-git`.
194
+
195
+ ```python
196
+ import rusty_git
197
+
198
+ # Ref advertisement for git-upload-pack (git clone / fetch)
199
+ advert = await rusty_git.advertise("/srv/repos/myorg/backend.git", "git-upload-pack")
200
+
201
+ # Handle pack negotiation and streaming
202
+ upload_res = await rusty_git.upload_pack("/srv/repos/myorg/backend.git", request_body)
203
+
204
+ # Ingest inbound git push packfile atomically
205
+ push_res = await rusty_git.receive_pack("/srv/repos/myorg/backend.git", push_body)
206
+ print("Updated Refs:", push_res["updated_refs"])
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Development & Testing
212
+
213
+ ### Rust Tests
214
+ ```bash
215
+ cargo test
216
+ ```
217
+
218
+ ### Python Integration Tests
219
+ ```bash
220
+ uv run pytest
221
+ ```
222
+
223
+ ---
224
+
225
+ ## License
226
+
227
+ Apache-2.0 © Framework M Contributors
@@ -0,0 +1,6 @@
1
+ rusty_git-0.3.0.dist-info/METADATA,sha256=nIa-NBENxvSy4vRBBnFoaQxudrTNCJkX1wFnTKx50Ps,7761
2
+ rusty_git-0.3.0.dist-info/WHEEL,sha256=rQOyhw13PhAFR9Q0_tF356_RmdK837INftPr-lyfJnE,98
3
+ rusty_git/_rusty_git.pyi,sha256=XOXipEiRsQhoRJ1-TTCIB_xzAzaqmIHwIw0QcrlKh4Y,6103
4
+ rusty_git/_rusty_git.pyd,sha256=6m3WHuSQfdzhRqAx4LNWH55mS_dR9KfHUxhwcvmr59c,13860352
5
+ rusty_git/__init__.py,sha256=pozmSzjh30xmhYQSkNU-Sik9y9QMeMK5Sv5ReavFeb4,2086
6
+ rusty_git-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: cargo-zigbuild-pack
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-abi3-win_amd64