rusty-git 0.3.0__cp312-abi3-pyemscripten_2024_0_wasm32.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,234 @@
1
+ Metadata-Version: 2.4
2
+ Name: rusty-git
3
+ Version: 0.3.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.12
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Programming Language :: Python :: 3.14
10
+ Classifier: Topic :: Software Development :: Version Control :: Git
11
+ Summary: High-speed pure Rust Git engine (gix + git2) for Python
12
+ Keywords: git,gix,pyo3,gitoxide,forge,libgit2
13
+ Author-email: Framework M <dev@frameworkm.dev>
14
+ License: Apache-2.0
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
17
+ Project-URL: Homepage, https://gitlab.com/framework-m/rusty-git
18
+ Project-URL: Issues, https://gitlab.com/framework-m/rusty-git/-/issues
19
+ Project-URL: Repository, https://gitlab.com/framework-m/rusty-git
20
+
21
+ # rusty-git
22
+
23
+ [![GitLab CI](https://gitlab.com/framework-m/rusty-git/badges/main/pipeline.svg)](https://gitlab.com/framework-m/rusty-git/-/pipelines)
24
+ [![PyPI version](https://img.shields.io/pypi/v/rusty-git.svg)](https://pypi.org/project/rusty-git/)
25
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
26
+
27
+ **High-speed pure Rust Git engine (`gix` + `git2`) compiled via PyO3 with both native `async` (Tokio) and GIL-released `sync` APIs.**
28
+
29
+ `rusty-git` provides in-process Git operations with **zero OS subprocess forks** and **statically-compiled native wheels** (no dynamic `libgit2.so` dependencies required).
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install rusty-git
37
+ # or with uv:
38
+ uv add rusty-git
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Quickstart: 100% In-Process Demo (Zero Subprocesses)
44
+
45
+ Copy-paste this into `example.py` to test immediately in any environment:
46
+
47
+ ```python
48
+ import asyncio
49
+ import os
50
+ import tempfile
51
+ import rusty_git
52
+
53
+
54
+ async def main():
55
+ with tempfile.TemporaryDirectory() as tmp_dir:
56
+ repo_path = os.path.join(tmp_dir, "demo.git")
57
+
58
+ # 1. Initialize a bare repository in-process (ZERO subprocesses)
59
+ await rusty_git.init_bare_repository(repo_path)
60
+
61
+ # 2. Create the initial root commit in-process
62
+ res = await rusty_git.create_commit(
63
+ repo_path,
64
+ branch="main",
65
+ files={
66
+ "README.md": b"# Hello from rusty-git!\n",
67
+ "src/main.py": b"print('Hello World')\n",
68
+ },
69
+ message="Initial commit",
70
+ author_name="Alice",
71
+ author_email="alice@example.com",
72
+ )
73
+ print("Root Commit SHA:", res["commit_sha"])
74
+
75
+ # 3. Inspect commit in-process
76
+ commit = await rusty_git.get_commit(repo_path, "HEAD")
77
+ print(f"Author: {commit['author_name']} <{commit['author_email']}>")
78
+ print(f"Message: {commit['message']}")
79
+
80
+ # 4. Read tree blob without disk checkout
81
+ blob = await rusty_git.get_tree_blob(repo_path, "HEAD", "README.md")
82
+ print("Blob Content:", blob.decode("utf-8").strip())
83
+
84
+ # 5. List branches
85
+ branches = await rusty_git.list_branches(repo_path)
86
+ print("Branches:", branches)
87
+
88
+
89
+ asyncio.run(main())
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Dual-Color Usage: Async & Sync
95
+
96
+ ### 1. Asynchronous API (Default - for Litestar, FastAPI, Granian, Uvicorn)
97
+
98
+ All blocking Git I/O is offloaded to a Tokio multi-threaded worker pool, releasing the Python GIL:
99
+
100
+ ```python
101
+ import asyncio
102
+ import rusty_git
103
+
104
+
105
+ async def main():
106
+ repo_path = "/path/to/your/repo"
107
+
108
+ # 1. Inspect commits
109
+ commit = await rusty_git.get_commit(repo_path, "HEAD")
110
+ print(commit["sha"], commit["message"], commit["author_name"])
111
+
112
+ # 2. Read tree blobs without disk checkout
113
+ readme = await rusty_git.get_tree_blob(repo_path, "main", "README.md")
114
+ print(readme.decode("utf-8"))
115
+
116
+ # 3. Fast unified diffs
117
+ hunks = await rusty_git.compare_refs(repo_path, "main~1", "main")
118
+ for hunk in hunks:
119
+ print(hunk["new_path"], hunk["content"])
120
+
121
+ # 4. 3-Way Merge
122
+ result = await rusty_git.execute_merge(
123
+ repo_path,
124
+ base_ref="main",
125
+ head_ref="feature",
126
+ author_name="Automated Merge",
127
+ author_email="bot@example.com",
128
+ )
129
+ if result["status"] == "merged":
130
+ print("Merged Commit SHA:", result["commit_sha"])
131
+
132
+
133
+ asyncio.run(main())
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 2. Synchronous API (`rusty_git.sync` - for CLI tools, Celery, scripts)
139
+
140
+ Direct execution on the calling thread with the Python GIL cleanly released (`py.detach`):
141
+
142
+ ```python
143
+ import rusty_git
144
+
145
+ # Read commit synchronously
146
+ commit = rusty_git.sync.get_commit("/path/to/repo.git", "HEAD")
147
+ print(commit["sha"], commit["author_name"])
148
+
149
+ # List branches synchronously
150
+ branches = rusty_git.sync.list_branches("/path/to/repo.git")
151
+ print("Branches:", branches)
152
+
153
+ # Read file directly from commit tree
154
+ blob = rusty_git.sync.get_tree_blob("/path/to/repo.git", "HEAD", "pyproject.toml")
155
+
156
+ # Create a commit directly on any branch (new or existing)
157
+ res = rusty_git.sync.create_commit(
158
+ "/path/to/repo.git",
159
+ branch="feature-x",
160
+ files={"hello.txt": b"Hello world!\n"},
161
+ message="feat: add hello.txt",
162
+ )
163
+
164
+ # 3-Way Merge synchronously
165
+ result = rusty_git.sync.execute_merge(
166
+ "/path/to/repo.git",
167
+ "main",
168
+ "feature",
169
+ author_name="Merge Bot",
170
+ author_email="bot@example.com",
171
+ )
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Architectural Note: Forges & Branch Management
177
+
178
+ `rusty-git` is designed for high-performance server-side forges and tools operating on **bare repositories**:
179
+
180
+ * **Creating branches with commits**: Passing any branch name to `create_commit(...)` will create or advance that branch atomically.
181
+ * **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>`.
182
+ * **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.
183
+
184
+ ### Engine Architecture: Pure `gix` vs. `git2` Wire Streaming
185
+
186
+ `rusty-git` intentionally separates user-facing operations from network pack streaming:
187
+
188
+ * **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.
189
+ * **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.
190
+
191
+ This keeps `rusty-git` focused entirely on performance-critical operations without unnecessary file-I/O wrapper overhead.
192
+
193
+ ---
194
+
195
+ ## Smart-HTTP Protocol Wire Support
196
+
197
+ `rusty-git` can drive Git HTTP and SSH protocol negotiation completely in-process:
198
+
199
+ > **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`.
200
+
201
+ ```python
202
+ import rusty_git
203
+
204
+ # Ref advertisement for git-upload-pack (git clone / fetch)
205
+ advert = await rusty_git.advertise("/srv/repos/myorg/backend.git", "git-upload-pack")
206
+
207
+ # Handle pack negotiation and streaming
208
+ upload_res = await rusty_git.upload_pack("/srv/repos/myorg/backend.git", request_body)
209
+
210
+ # Ingest inbound git push packfile atomically
211
+ push_res = await rusty_git.receive_pack("/srv/repos/myorg/backend.git", push_body)
212
+ print("Updated Refs:", push_res["updated_refs"])
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Development & Testing
218
+
219
+ ### Rust Tests
220
+ ```bash
221
+ cargo test
222
+ ```
223
+
224
+ ### Python Integration Tests
225
+ ```bash
226
+ uv run pytest
227
+ ```
228
+
229
+ ---
230
+
231
+ ## License
232
+
233
+ Apache-2.0 © Framework M Contributors
234
+
@@ -0,0 +1,7 @@
1
+ rusty_git/__init__.py,sha256=pozmSzjh30xmhYQSkNU-Sik9y9QMeMK5Sv5ReavFeb4,2086
2
+ rusty_git/_rusty_git.abi3.so,sha256=j1ppObruPwc3zAUGsedFCbcAyzvQQKkgikzb8bjdQ6c,4520875
3
+ rusty_git/_rusty_git.pyi,sha256=XOXipEiRsQhoRJ1-TTCIB_xzAzaqmIHwIw0QcrlKh4Y,6103
4
+ rusty_git-0.3.0.dist-info/sboms/rusty-git.cyclonedx.json,sha256=gkQkSR54NMRooGbpfiOkW5J_NItzbDVM-H9maDl1iSw,206837
5
+ rusty_git-0.3.0.dist-info/METADATA,sha256=ogkqfE3Y6vMr8tV361_MF5LFHKYvGhmBTBBq15FUSf4,8104
6
+ rusty_git-0.3.0.dist-info/WHEEL,sha256=xoVwukNZX5x0STCtBOeW-Whs4wuCh7O9bG4M5ei4aFI,114
7
+ rusty_git-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.16.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-abi3-pyemscripten_2024_0_wasm32
5
+