Skip to content

gitcache: decide which git fetches can be cached, and their key - #163

Merged
linyiru merged 6 commits into
mainfrom
feat/git-cache-server
Nov 5, 2026
Merged

linyiru merged 6 commits into
mainfrom
feat/git-cache-server

Conversation

@linyiru

@linyiru linyiru commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

First part of the git checkout cache for EC2 tiers. Nothing calls this yet.

Parse reads the body of a git-upload-pack POST and accepts it only when the response depends on the request alone:

  • protocol v2 command=fetch
  • no have or shallow, so the client has no objects
  • done present, so negotiation is finished
  • want lines holding complete object IDs
  • only lines it knows

Everything else returns ErrNotCacheable and goes to GitHub. It is an allow list, so a line from a newer git costs a miss, not a wrong answer. Three things are refused because their answer moves with the repository: want-ref, include-tag, and sparse:oid filters, alone or inside combine:.

Key hashes the repository and what changes the objects: wants, depth, filter, thin-pack, ofs-delta and the object format. It leaves out agent, session-id and no-progress, so git versions and actions/checkout's arguments share an entry. Real requests from git 2.56 are the test's fixtures; git fetch --depth=1 and actions/checkout's arguments send the same lines.

Why no cache-server change

An entry there is already repository, ref, key and version, with scope checks, eviction, size limits and S3. The instance's proxy will call the same API as actions/cache, with git/<hash> as the key, so isolation is inherited and the security surface does not grow.

Verified

  • 100% statement coverage, and FuzzParse for 15 s.
  • scripts/mutate.sh against 23 guards. The first pass left three surviving, a missing two-command case, unordered wants and a field-separation test that proved nothing. All three now fail a test.

Replaying through real git was done in a proof of concept, not here. That belongs with the proxy.

A git checkout cache needs to tell a fetch whose answer depends on the
request alone from one whose answer does not. Parse accepts a protocol
v2 fetch only when it understands every line, the client has no objects,
and the wants are exact commits. Anything else goes to GitHub, so a line
from a newer git costs a miss rather than a wrong answer, and want-ref,
whose answer moves, is never cached.

Nothing uses this yet. The instance's proxy will store through
cache-server's existing API, which already scopes entries by repository
and ref, so this adds no endpoint.

Signed-off-by: Yi-Ru Lin <deduce@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Ref-dependent filters and include-tag can produce different object sets under the same cache key, while duplicate commands and malformed flag arguments are incorrectly accepted.

4 open issues
What changed in this PR

Introduces the request-classification and cache-key foundation for EC2 git checkout caching. The new package is not yet connected to a proxy or cache server.

Changes:

  • Parses protocol v2 fetch requests and rejects unsupported request forms.
  • Generates repository-scoped keys from fetch options and normalizes repository paths.
  • Adds parsing, key-isolation, repository-path, and fuzz tests.
File Description
internal/​gitcache/​request.go Implements fetch parsing, cache keys, and repository normalization.
internal/​gitcache/​request_test.go Tests accepted and rejected requests, key behavior, and parser invariants.
AGENTS.md Adds the gitcache package to the repository directory guide.

Review effort: Balanced

Comment thread internal/gitcache/request.go Resolved
Comment thread internal/gitcache/request.go Resolved
Comment thread internal/gitcache/request.go Resolved
Comment thread internal/gitcache/request.go Resolved
sparse:oid can name its pattern through a ref, such as HEAD:patterns, and
combine: can hold one, so the same request can mean different objects
after that ref moves. Allow blob:none, blob:limit and tree alone.

Signed-off-by: Yi-Ru Lin <deduce@gmail.com>
Its answer holds whatever annotated tags point at the wants, which
changes without the request changing.

Signed-off-by: Yi-Ru Lin <deduce@gmail.com>
upload-pack rejects it, so a hit must not answer it.

Signed-off-by: Yi-Ru Lin <deduce@gmail.com>
Cutting at the space accepted `done extra` as done, which upload-pack
rejects.

Signed-off-by: Yi-Ru Lin <deduce@gmail.com>
Signed-off-by: Yi-Ru Lin <deduce@gmail.com>
@linyiru

linyiru commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

babysit-pr · head 342bed6
Fixed: #163 (comment) — filters that can move are refused, e0b9328, 342bed6
Fixed: #163 (comment) — include-tag is refused, 71f90d7
Local review (subagent): 4566334..342bed6 — none
CI: running on this head

@linyiru
linyiru merged commit 4c22b6a into main Nov 5, 2026
7 checks passed
@linyiru
linyiru deleted the feat/git-cache-server branch November 5, 2026 20:12
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.

2 participants