rusty-git
High-speed pure Rust Git engine (gix + git2) compiled via PyO3 with both native async (Tokio) and GIL-released sync APIs.
rusty-git provides in-process Git operations with zero OS subprocess forks and statically-compiled native wheels (no dynamic libgit2.so dependencies required).
Installation
pip install rusty-git
# or with uv:
uv add rusty-git
Quickstart: 100% In-Process Demo (Zero Subprocesses)
Copy-paste this into example.py to test immediately in any environment:
import asyncio
import os
import tempfile
import rusty_git
async def main():
with tempfile.TemporaryDirectory() as tmp_dir:
repo_path = os.path.join(tmp_dir, "demo.git")
# 1. Initialize a bare repository in-process (ZERO subprocesses)
await rusty_git.init_bare_repository(repo_path)
# 2. Create the initial root commit in-process
res = await rusty_git.create_commit(
repo_path,
branch="main",
files={
"README.md": b"# Hello from rusty-git!\n",
"src/main.py": b"print('Hello World')\n",
},
message="Initial commit",
author_name="Alice",
author_email="alice@example.com",
)
print("Root Commit SHA:", res["commit_sha"])
# 3. Inspect commit in-process
commit = await rusty_git.get_commit(repo_path, "HEAD")
print(f"Author: {commit['author_name']} <{commit['author_email']}>")
print(f"Message: {commit['message']}")
# 4. Read tree blob without disk checkout
blob = await rusty_git.get_tree_blob(repo_path, "HEAD", "README.md")
print("Blob Content:", blob.decode("utf-8").strip())
# 5. List branches
branches = await rusty_git.list_branches(repo_path)
print("Branches:", branches)
asyncio.run(main())
Dual-Color Usage: Async & Sync
1. Asynchronous API (Default - for Litestar, FastAPI, Granian, Uvicorn)
All blocking Git I/O is offloaded to a Tokio multi-threaded worker pool, releasing the Python GIL:
import asyncio
import rusty_git
async def main():
repo_path = "/path/to/your/repo"
# 1. Inspect commits
commit = await rusty_git.get_commit(repo_path, "HEAD")
print(commit["sha"], commit["message"], commit["author_name"])
# 2. Read tree blobs without disk checkout
readme = await rusty_git.get_tree_blob(repo_path, "main", "README.md")
print(readme.decode("utf-8"))
# 3. Fast unified diffs
hunks = await rusty_git.compare_refs(repo_path, "main~1", "main")
for hunk in hunks:
print(hunk["new_path"], hunk["content"])
# 4. Dry run: show conflicts before anyone clicks "Merge"
preview = await rusty_git.preview_merge(repo_path, base_ref="main", head_ref="feature")
if preview["status"] == "conflict":
print("Conflicts:", preview["conflicts"]) # nothing was written
else:
print("Would produce tree:", preview["tree_sha"])
# 5. 3-Way Merge (update_ref=True moves `main` to the result atomically).
# strategy: "merge" (default) | "squash" | "fast-forward"
result = await rusty_git.execute_merge(
repo_path,
base_ref="main",
head_ref="feature",
author_name="Automated Merge",
author_email="bot@example.com",
update_ref=True,
strategy="squash",
)
if result["status"] == "merged":
print("Merged Commit SHA:", result["commit_sha"], "ref moved:", result["ref_updated"])
elif result["status"] == "up-to-date":
print("Already up to date at", result["commit_sha"])
asyncio.run(main())
2. Synchronous API (rusty_git.sync - for CLI tools, Celery, scripts)
Direct execution on the calling thread with the Python GIL cleanly released (py.detach):
import rusty_git
# Read commit synchronously
commit = rusty_git.sync.get_commit("/path/to/repo.git", "HEAD")
print(commit["sha"], commit["author_name"])
# List branches synchronously
branches = rusty_git.sync.list_branches("/path/to/repo.git")
print("Branches:", branches)
# Read file directly from commit tree
blob = rusty_git.sync.get_tree_blob("/path/to/repo.git", "HEAD", "pyproject.toml")
# Create a commit directly on any branch (new or existing)
res = rusty_git.sync.create_commit(
"/path/to/repo.git",
branch="feature-x",
files={"hello.txt": b"Hello world!\n"},
message="feat: add hello.txt",
)
# 3-Way Merge synchronously (strategy: "merge" | "squash" | "fast-forward")
result = rusty_git.sync.execute_merge(
"/path/to/repo.git",
"main",
"feature",
author_name="Merge Bot",
author_email="bot@example.com",
update_ref=True, # move `main` to the merge result under the ref lock
strategy="fast-forward", # raise GitRepositoryError instead of merging
)
# Dry run first - writes nothing (no tree, commit, or ref)
preview = rusty_git.sync.preview_merge("/path/to/repo.git", "main", "feature")
print(preview["status"], preview["conflicts"], preview["tree_sha"])
Architectural Note: Forges & Branch Management
rusty-git is designed for high-performance server-side forges and tools operating on bare repositories:
- Creating branches with commits: Passing any branch name to
create_commit(...)will create or advance that branch atomically. - Creating a branch without a commit:
update_ref(repo_path, "feature", sha)movesrefs/heads/featurethrough agixref transaction — it takes the same ref locks asgit, appends to the reflog, and keepspacked-refsconsistent. Never writerefs/heads/*by hand: that bypasses locking, races with concurrent pushes, and resurrects deleted refs once the name is also inpacked-refs. Passexpected_old=""to create-only, a 40-hex SHA for compare-and-swap, andtarget=Noneto delete. Non-fast-forward branch moves are refused unlessforce=True. - Switching active branch (
HEAD):set_head(...)moves the symbolicHEADreference under the same transaction. Editing theHEADfile directly has the same hazards as writing a branch by hand. - Merge strategies:
execute_merge(..., strategy=...)offers the three forge buttons."merge"(default) fast-forwards when it can and otherwise writes a two-parent merge commit;"squash"always writes exactly one commit on the base, defaulting to the concatenated feature commit summaries;"fast-forward"refuses anything that is not a fast-forward withGitRepositoryError(cannot fast-forward). A head that is already contained in the base reports"up-to-date"— a success, not a conflict. - Dry-run merges:
preview_merge(repo_path, base_ref, head_ref)runs the same ORT tree merge and writes nothing — no tree, no commit, no ref — returningstatus,conflicts, and thetree_shathe merge would produce. Use it to show conflicts before the user clicks "Merge".
Engine Architecture: Pure gix vs. git2 Wire Streaming
rusty-git intentionally separates user-facing operations from network pack streaming:
- 100% Pure
gixCore (WebAssembly / Pyodide Ready):commit(atomic in-memory trees & commits),inspect(refs, tree blobs, histogram unified diffs),merge(3-way ORT tree merge, dry-run previews, and themerge/squash/fast-forwardstrategies) andops(has_object,repository_stats,fsck,pack_refs) run entirely on pure-Rustgix. They require zero C headers and run in browsers and Pyodide. - Wire Protocol Pack Streaming (
git2): Ingesting thin packs (git push) and streaming sliding-window delta packs (git clone/git fetch) uselibgit2's battle-tested pack engine. Oncegix-packhigh-level streaming pack generation reaches full maturity, these remaining wire endpoints will transition togix, completing the journey to 100% pure Rust. - Native-only maintenance (
git2):repackandgcreuse that same pack engine — folding a whole object database into one delta-compressed pack is exactly the code not to hand-roll — so thegcmodule is compiled only for non-wasm32targets (#[cfg(not(target_arch = "wasm32"))]), like the wire modules.fork_repositorystays portable: it copies object and ref files (gix+std::fs) and never rewrites a pack.
This keeps rusty-git focused entirely on performance-critical operations without unnecessary file-I/O wrapper overhead.
Scope and Non-Goals
rusty-git is a Git engine, not a forge and not a web framework. That boundary
is deliberate: the library does Git work in-process and the application owns
everything around it.
In scope
- In-process Git object, ref and merge operations on bare repositories —
create_commit,update_ref,set_head,execute_merge,preview_merge, theinspectread surface, and theopsmaintenance primitives (has_object,repository_stats,fsck,pack_refs,fork_repository,repack,gc— the last two native-only, see the architecture note above). - Smart-HTTP wire serving as bytes in / bytes out (
advertise,upload_pack,receive_pack) with zero OS subprocesses. - Engine paths that stay pure
gixand therefore compile forwasm32(Pyodide) as well as native.
Out of scope - the application's job
| Item | Why it does not belong here |
|---|---|
| HTTP transport, routing, auth, TLS | Not Git. The wire API is deliberately bytes-in/bytes-out; sockets, certificates and sessions belong to the server. |
Hook execution (pre-receive etc.) |
Needs a process model and a trust boundary. The server should expose results, not run arbitrary code in-process. |
Working-tree checkout / reset / clean |
This is a bare-repository library: no index, no worktree. create_commit builds trees in memory by design. |
Submodule recursion / submodule update |
Requires network and a worktree; list_tree reports gitlinks, which is all a forge UI needs. |
| Git LFS | A separate protocol (batch API) plus a storage service. |
| GPG/SSH signature verification | Needs keyrings and a trust policy supplied by the application. |
| Code search, syntax highlighting, diff rendering | Application/UI concerns; compare_refs returns data, not presentation. |
| PR/MR/issue objects, review comments, webhooks, notifications | Product model, not Git. |
Interactive rebase / cherry-pick / git am machinery |
preview_merge plus create_commit cover the forge merge-button strategies; interactive history rewriting is out of scope. |
| Repository deletion or renaming on disk | Plain filesystem operations with no locking subtlety to encapsulate. |
Credential helpers, ~/.gitconfig handling |
The library takes explicit arguments and never reads user configuration. |
If a downstream project asks for one of these, the answer is the table above plus
the recommended application-side approach - not a new export. The repo-root
TODO-missing.md is the backlog and outcome log for the gaps that are in scope,
and a boundary change has to be recorded there before it becomes a task.
Smart-HTTP Protocol Wire Support
rusty-git can drive Git HTTP and SSH protocol negotiation completely in-process:
Note on Transport & Auth (SSH / TLS):
rusty-gitoperates directly on request/response byte streams rather than managing sockets. Transport encryption and authentication (SSH keys viaasyncssh/OpenSSH, or TLS/mTLS client certs via Nginx/Caddy/HTTPX) happen cleanly at the transport layer before passing payloads torusty-git.
import rusty_git
# Ref advertisement for git-upload-pack (git clone / fetch)
advert = await rusty_git.advertise("/srv/repos/myorg/backend.git", "git-upload-pack")
# Handle pack negotiation and streaming
upload_res = await rusty_git.upload_pack("/srv/repos/myorg/backend.git", request_body)
# Ingest inbound git push packfile atomically
push_res = await rusty_git.receive_pack("/srv/repos/myorg/backend.git", push_body)
print("Updated Refs:", push_res["updated_refs"])
Operations & Maintenance
Seven operations a long-lived forge needs, all in-process and exposed at the
package root and on rusty_git.sync (they live in the ops and gc Rust
modules):
import rusty_git
# Do we already have this object? (index lookup, no object is inflated)
if not await rusty_git.has_object("/srv/repos/app.git", sha):
...
# Dashboard numbers: refs, branches, tags, loose objects, packs, size
stats = await rusty_git.repository_stats("/srv/repos/app.git", deep=True)
print(stats["branch_count"], stats["object_count"], stats["disk_size_bytes"])
# Integrity check before a user finds the damage
report = await rusty_git.fsck("/srv/repos/app.git")
if not report["ok"]:
print(report["errors"]) # sorted, each names the object and the ref/edge
print(report["dangling"]) # unreachable objects: reported, not an error
# Fold thousands of loose ref files into one sorted `packed-refs`
packed = await rusty_git.pack_refs("/srv/repos/app.git") # 0 => already packed
# Fork: copy objects (hardlinked by default) and refs into a new bare repo
await rusty_git.fork_repository("/srv/repos/app.git", "/srv/repos/app-fork.git")
# Compact: fold every pack into one (a crash can only leave debris, never damage)
print(
await rusty_git.repack("/srv/repos/app.git")
) # {"packs_before": 12, "packs_after": 1, "objects": 431}
# Full maintenance: repack + pack-refs + de-duplicate loose objects.
# Pruning is opt-in because "unreachable" is not "unwanted": pass a grace period.
stats = await rusty_git.gc("/srv/repos/app.git", prune_expire_days=14)
print(stats["pack_count"], stats["loose_objects"], stats["disk_size_bytes"])
What each operation promises, and what it does not
-
has_objectanswers from the object index (loose names and pack indexes), soTruemeans "present", not "decodes". A sha that is not 40 hex characters raisesValueError— a typo must not look like an absent object. -
repository_statsreads the layout forloose_objects,pack_countanddisk_size_bytes(so the numbers matchgit count-objects/du) and the object store forobject_count.object_countisNoneunlessdeep=Truebecause it walks the whole store and holds one hash set of ids: counts and sizes are O(files),object_countis O(objects) in time and memory.disk_size_bytescovers the git directory (.gitfor a non-bare repo), never the worktree, and isNoneunder Pyodide. -
fsckdetects two classes of damage: dangling references (a ref, commit parent, commit tree, tag target or tree entry naming an object that is absent) and — withstrict=True, the default — undecodable objects. It does not verify pack trailer hashes or recompute object ids, so a valid pack holding an object under the wrong id is invisible here; that is the same trade-offgit fsckmakes without--full.strict=Falseis a connectivity check that still reads object headers (the walk cannot descend without them) but never decodes or validates a payload — useful as a cheap pre-push gate. Submodule (gitlink) targets are not required to exist, exactly as ingit fsck. Peak memory is two hash sets of object ids, a few dozen bytes per object. -
pack_refsfolds the loose ref files underrefs/into one sortedpacked-refs(# pack-refs with: peeled fully-peeled sorted,^-peeled annotated tags, atomic rename) and returns how many refs it moved;0means the store was already packed, so calling it twice is a no-op. It goes throughgix's packed-ref transaction, which takespacked-refs.lock, renames the new file into place and only then deletes the loose files — an interruption leaves a duplicate ref, never a missing one, and a lock held elsewhere for more than a second is an error rather than a bypass.all=True(default) packs everything;all=Falsepacks onlyrefs/tags/*, matchinggit pack-refswithout--all, which keeps actively-updated branches loose and therefore cheap to compare-and-swap.HEADand other symbolic refs stay untouched (the format cannot represent them), and no reflog entry is written because no ref value changes.git show-refoutput is identical afterwards, andupdate_refkeeps working on the packed entries — includingtarget=None, which removes thepacked-refsline. -
fork_repositoryonly ever reads the source, so it is safe to fork a repository that is serving traffic. Objects are copied before refs: a push landing mid-copy therefore makes the fork stale, never inconsistent — re-fork to catch up.hardlink=True(the default) hardlinks object files, so the fork costs ref metadata instead of a full copy and stays readable even if the source is later pruned or deleted; filesystems that refuse a link (EXDEV,EPERM,FAT/SMB) fall back to a copy per file. Refs,HEAD,packed-refsandshalloware always copied, never linked — a shared ref file would let a push to the fork move a branch in the source.configis deliberately not copied (same reasongit clonerewrites it): configure the fork yourself. A destination that exists and is not empty is refused withGitRepositoryError, and so is one that is, contains, or sits inside the source. -
repackfolds every object a reference (orHEAD) reaches into one pack, writes it beside the existing packs,fsyncs it, and only then deletes the packs it superseded — so a crash at any point leaves a readable repository, at worst with unreferenced debris that the next run folds in. A pack only becomes usable when its.idxexists, so a concurrentlist_commitscannot see a half-written pack, and a reader already walking history keeps its open files alive.packs_after == 1means the compaction worked;objectsis the size of the new pack, which equalsgit rev-list --objects --all. Packs carrying a.keepfile are left alone, and unreachable objects are neither copied nor deleted. Requiresgit2's pack builder, so it is native-only (no Pyodide). -
gcrunsrepack, folds loose refs intopacked-refs, and deletes the loose objects the new pack made redundant — losing the redundant file is what actually shrinks the repository. Pruning is opt-in:prune_expire_daysdefaults toNone("never delete"), because "no ref reaches it" does not mean "nothing needs it" — a push in flight and anything a reflog could recover are both unreachable and both young, so anything younger than the period survives whatever you pass.prune_expire_days=0is for a caller who has just read thedanglinglist fromfsck. Packs are never deleted beyond the ones the repack superseded. Run onegcper repository at a time: it does not takegit gc'sgc.pidlock, so a scheduled task should own the repository, not a request handler. -
Push quarantine —
receive_packindexes the inbound pack into a private object database (objects/incoming-<unique>/, a throwaway bare repository whoseobjects/info/alternatesnames the real one, so thin-pack fix-up still resolves) and only migrates those objects into the real ODB once a ref is actually going to move. A refused push — non-fast-forward, stale lease, malformed pack — therefore leaves the repository byte-for-byte unchanged, which is what stops a client that can push from growing it without bound. The quarantine is deleted on every path, including a panic. Passreceive_pack(..., quarantine=False)for the previous behaviour (ingest straight into the real ODB); it is kept for one release as a fallback.
Not implemented, by design
- Server-side
clonefrom a remote URL — there is no network transport in this build:gix's clone/fetch path is gated behindblocking-network-client/async-network-client, and this crate enables neither (compilingPrepareFetch::fetch_onlyagainst the shipped feature set fails witherror[E0599]). Enabling one means adding a TLS stack and credentials handling to a statically-linked wheel — a packaging decision, not an API gap. Import out of band (git clone --mirrorin the pipeline, or a one-shot fetch in the application), then usefork_repositoryfor in-process copies. - Reflog expiry, commit-graph and multi-pack-index writing —
gcprunes by ref-reachability plus a grace period, deliberately not by reflog reachability: it never expires a reflog, so anything a reflog could recover is protected by age alone. Treat the grace period as the safety margin thatgit gcgets from its reflog expiry.
Development & Testing
Rust Tests
cargo test
Python Integration Tests
uv run pytest
License
Apache-2.0 © Framework M Contributors
Metadata
Release files for rusty-git 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| rusty_git-0.4.0.tar.gz | 180.6 kB | Without provenance | Details for rusty_git-0.4.0.tar.gz |
Built distributions (wheels)
Total release size: 31.0 MB
Release files / rusty_git-0.4.0.tar.gz
| Download URL | rusty_git-0.4.0.tar.gz |
|---|---|
| Size | 180.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1ec57c0779cc001efef9fed8935364b73ac6a2761c301d25b672e8714a89d1d
|
|
BLAKE2b-256 checksum How to use checksums |
d589b4110cdbc77278ace5ab0f4ae04d041d931f74b9f83f06844a8391fe34d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-win_amd64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-win_amd64.whl |
|---|---|
| Size | 6.2 MB |
| Tags | CPython 3.12 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0e608a11177d7325141f36d76a45909a9f847f45458462c409192ebf72606f6b
|
|
BLAKE2b-256 checksum How to use checksums |
8427afde8a698f953528d2b4254d5c152c35196dfd9a2855f18d235507e7d6d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-pyemscripten_2024_0_wasm32.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-pyemscripten_2024_0_wasm32.whl |
|---|---|
| Size | 1.9 MB |
| Tags | CPython 3.12 PyEmscripten 2024.0+ WebAssembly abi3 |
|
SHA-256 checksum How to use checksums |
0029b4a0086db42c65c7447a92cd774c62d008680569b04de43a28ab163226ae
|
|
BLAKE2b-256 checksum How to use checksums |
ff8985828fa1c877c16b235cd5cee5361c653851381aa8f564e3918559d6b96b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-musllinux_1_2_x86_64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 3.9 MB |
| Tags | CPython 3.12 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
cb73cd682524ab4d468934cda366cac95b7a454a0841f2e632243bce7cac2d9a
|
|
BLAKE2b-256 checksum How to use checksums |
44ff847eb4671a1db1d80cd5b2b7c299f3871ef4571fa4d37a1aaa9d097ba4e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-musllinux_1_2_aarch64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 3.6 MB |
| Tags | CPython 3.12 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
7f7d941a58b3f35bc8b38c3e246ff2ef27881dce5fcdd3ddc0eaab42d35ce1ec
|
|
BLAKE2b-256 checksum How to use checksums |
360ecb7c71fac9f34fe8a69e5fa36491408ce86b32662fa26b9d26f8db46e376
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
|---|---|
| Size | 3.8 MB |
| Tags | CPython 3.12 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
180b2b213f6199d188b3c7239f8da954100d30839b40b1dae9c5870e2ac94624
|
|
BLAKE2b-256 checksum How to use checksums |
dd1566d1771edf58b5aeb40f51d01afc017a424490856075e5db18e08a316c34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
|---|---|
| Size | 3.6 MB |
| Tags | CPython 3.12 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
20e866f34faffbe176bb745ff963e7395006e168d4b6205d7b66084d1a67a3c7
|
|
BLAKE2b-256 checksum How to use checksums |
246d52d32f78801fb6b507cebf3e7864059873ae11059779b5ac9c34dc08edd8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-macosx_11_0_arm64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 3.8 MB |
| Tags | CPython 3.12 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
3d40d2486415632876b812a1230079e29636678ea829697ef802176e8be7382e
|
|
BLAKE2b-256 checksum How to use checksums |
99e923dbbb536fd3920d80c1d2bac8afce9132b8fe98eceda59f2822822b3736
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Release files / rusty_git-0.4.0-cp312-abi3-macosx_10_12_x86_64.whl
| Download URL | rusty_git-0.4.0-cp312-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 4.0 MB |
| Tags | CPython 3.12 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3dd60def90def995b26a47ac658700d71941270c8936f4edce297dfe58422daa
|
|
BLAKE2b-256 checksum How to use checksums |
09fe53550c60d0e79c18f2a75dc2b5e7aefa4b435f5f38e23c0b68d5c53e0752
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.16
|
Security for rusty-git 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.