1
0
Fork 0
mirror of https://github.com/imjasonh/infinite-git synced 2026-07-11 18:31:27 +00:00

Fix four bugs: packfile offset tracking, HTTP error ordering, ref ordering, and race condition

- packfile.go: ReadObject now uses a countingReader to track compressed
  bytes consumed and properly advances r.offset, fixing sequential
  multi-object reads.
- handlers.go: Use commitSHA from GenerateCommit directly instead of
  re-reading refs, which fixes both the HTTP error-after-body-written
  issue and ensures HEAD is always advertised first (Git protocol
  requirement).
- commit.go: GenerateCommit now holds the repo lock for the entire
  read-modify-write cycle, preventing concurrent generates from reading
  the same parent and losing ref updates.
- repo.go: Added Lock/Unlock/GetRefsLocked methods to support holding
  the mutex across multiple repo operations.

https://claude.ai/code/session_015F9BYQoCy2P2zYZBuVeXHH
This commit is contained in:
Claude 2026-04-02 05:52:24 +00:00
parent 09281ac00c
commit 29666530cc
No known key found for this signature in database
4 changed files with 60 additions and 36 deletions

View file

@ -22,9 +22,7 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
}
// Generate a new commit before advertising refs
s.mu.Lock()
commitSHA, err := s.generator.GenerateCommit()
s.mu.Unlock()
if err != nil {
log.Error("failed to generate commit", "error", err)
@ -51,31 +49,19 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
return
}
// Get current refs
refs, err := s.repo.GetRefs()
if err != nil {
log.Error("failed to get refs", "error", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
// Use the commitSHA directly from GenerateCommit rather than re-reading
// refs. This avoids a race where concurrent requests could all see the
// same latest ref, and ensures HEAD is always advertised first.
capabilities := strings.Join(s.repo.GetCapabilities(), " ")
// Advertise HEAD first (Git protocol requirement), then refs/heads/main.
if err := pw.Writef("%s HEAD\x00%s\n", commitSHA, capabilities); err != nil {
log.Error("failed to write HEAD ref", "error", err)
return
}
// Write capabilities with first ref
capabilities := strings.Join(s.repo.GetCapabilities(), " ")
first := true
for ref, oid := range refs {
if first {
if err := pw.Writef("%s %s\x00%s\n", oid, ref, capabilities); err != nil {
log.Error("failed to write ref with capabilities", "error", err)
return
}
first = false
} else {
if err := pw.Writef("%s %s\n", oid, ref); err != nil {
log.Error("failed to write ref", "error", err)
return
}
}
if err := pw.Writef("%s refs/heads/main\n", commitSHA); err != nil {
log.Error("failed to write main ref", "error", err)
return
}
// Final flush