Skip to content

feat(008): replace git binary shell-outs with in-process gix implementation#147

Merged
juliuskrah merged 5 commits into
mainfrom
008-remove-git-shellouts
May 13, 2026
Merged

feat(008): replace git binary shell-outs with in-process gix implementation#147
juliuskrah merged 5 commits into
mainfrom
008-remove-git-shellouts

Conversation

@juliuskrah
Copy link
Copy Markdown
Contributor

Summary

  • Replace all four std::process::Command::new("git") shell-out call sites in http_git_server.rs with HttpPackServer — a new in-process implementation of the HTTP smart protocol (upload-pack and receive-pack) using gix-pack, gix-packetline, and gix-protocol
  • Remove the git binary from the docker/git-service.Dockerfile runtime stage (~30 MB image reduction)
  • Delete scripts/init-demo-catalog.sh and remove all documentation references to it
  • Add two CI grep gates that permanently block re-introduction of git shell-outs or the init script

What changed

Area Change
src/git/pack_server.rs New HttpPackServer with 4 methods: advertise/handle for upload-pack and receive-pack
src/http_git_server.rs All 4 call sites replaced; DefaultBodyLimit applied to receive-pack route (HTTP 413 on oversize)
docker/git-service.Dockerfile git removed from runtime apk add; stale safe.directory config line removed
scripts/init-demo-catalog.sh Deleted
docs/ (3 files) All init-demo-catalog.sh references removed
.github/workflows/ci.yml New shellout-guards job with grep gates
tests/integration/http_smart_protocol.rs 8 new integration tests (clone, fetch, push, rejection, size limit, atomicity)

Test plan

  • All 72 tests pass locally: cargo test (60 unit + 8 HTTP smart-protocol integration + 4 gRPC integration)
  • cargo fmt --all -- --check — clean
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • grep -rn 'Command::new("git")' gitstore-git-service/src/ — no matches
  • Docker image built; which git inside container exits non-zero
  • Service starts and logs healthy inside the container

Closes #109

🤖 Generated with Claude Code

…tation

Replace all four std::process::Command::new("git") call sites in
http_git_server.rs with a new HttpPackServer that implements the HTTP
smart protocol (upload-pack and receive-pack) entirely in-process using
gix-pack, gix-packetline, and gix-protocol sub-crates.

- Add HttpPackServer in src/git/pack_server.rs with advertise_upload_pack_refs,
  handle_upload_pack, advertise_receive_pack_refs, and handle_receive_pack
- Enforce configurable max pack size via DefaultBodyLimit (HTTP 413 on oversize)
- Atomic ref-update transactions with fast-forward checks and rollback on failure
- Remove git binary from docker/git-service.Dockerfile runtime stage
- Delete scripts/init-demo-catalog.sh and remove all doc references to it
- Add CI grep gates: no Command::new("git") in src/, no init-demo-catalog refs
- Add 8 integration tests covering clone, fetch, push, rejection, size limit,
  and atomicity (all pass without git binary on the test host PATH)

Closes GH#109

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d7d7e123b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gitstore-git-service/src/git/pack_server.rs Outdated
Comment thread gitstore-git-service/src/git/pack_server.rs Outdated
juliuskrah and others added 3 commits May 13, 2026 22:07
Without symref=HEAD:refs/heads/main in the capability string, git
clients on Linux do not set up a local tracking branch after clone,
leaving them in a detached HEAD state. This caused clone tests to fail
(README.md missing) and push tests to fail with "src refspec main does
not match any" on x86_64-linux CI runners.

Build the capability string dynamically so HEAD symbolic targets are
always reflected correctly regardless of the default branch name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On Linux, gix's platform.all() does not yield HEAD as a regular ref
(HEAD is a special pseudo-ref, not stored in packed-refs or loose refs).
On macOS it happened to appear, masking the bug locally.

Without HEAD in the advertisement, git clone prints "remote HEAD refers
to nonexistent ref, unable to checkout" and creates an empty working
tree (exits 0 but no files). This caused three integration tests to
fail on x86_64-linux CI.

Fix: resolve HEAD explicitly via repo.head_id() and prepend it to the
ref list, skipping it if encountered again during platform.all().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
P1: stream pack data in ≤65511-byte sideband-64k chunks so large packs
(> 64 KiB) are not truncated by the pkt-line 65516-byte limit.

P2: write incoming pack bytes to a temp quarantine directory before
touching refs; only move the pack/index into the live ODB after the
atomic ref transaction commits, so a rejected push leaves no orphan
objects on disk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

@juliuskrah juliuskrah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both P1 and P2 issues resolved in commit 3f7dce2.

P1 (sideband chunking): handle_upload_pack now iterates pack_data.chunks(65511) and emits one sideband pkt-line per chunk, staying within the 65516-byte pkt-line hard limit regardless of pack size.

P2 (object quarantine): handle_receive_pack now stages incoming pack bytes into a tempfile::TempDir quarantine directory via stage_pack_to_quarantine before touching any refs. The temp pack/index files are only moved into the live ODB pack directory (promote_quarantine) after repo.edit_references commits successfully. A failed ref transaction (stale old_oid, non-fast-forward, etc.) causes the TempDir to be dropped automatically, leaving the repository object store unchanged.

@juliuskrah
Copy link
Copy Markdown
Contributor Author

@codex review

1 similar comment
@juliuskrah
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f7dce2f47

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gitstore-git-service/src/git/pack_server.rs
Comment thread gitstore-git-service/src/git/pack_server.rs Outdated
Comment thread gitstore-git-service/src/git/pack_server.rs
P1 (line 136): ref-delete commands (new_oid == 000…000) now produce
Change::Delete instead of Change::Update, so `git push origin :branch`
is handled correctly.

P1 (line 152): pre-receive / update / post-receive lifecycle points are
now called in handle_receive_pack. All three are no-op skeletons for
now; future tasks will route them to gitstore-api over gRPC.

P2 (line 378): parse_wants_and_haves replaces parse_wants so `have`
lines from the client are collected; build_pack_for_wants uses
rev_walk().with_boundary(haves) to exclude already-known commits,
restoring incremental fetch behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@juliuskrah juliuskrah added this pull request to the merge queue May 13, 2026
Merged via the queue into main with commit fc05d8a May 13, 2026
27 of 28 checks passed
@juliuskrah juliuskrah deleted the 008-remove-git-shellouts branch May 13, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Initiative] Remove git binary shell-outs from gitstore-git-service

1 participant