mirror of
https://github.com/imjasonh/infinite-git
synced 2026-07-11 10:21:25 +00:00
Implement pure Go Git HTTP server without CLI dependencies
Major rewrite to eliminate git CLI dependency: - Created object package for Git object format handling (blob, tree, commit) - Implemented packfile generation from scratch - Rewrote repo initialization to create Git repos without CLI - Implemented git-upload-pack protocol in pure Go - Updated all server handlers to use new implementation - Added Git packet tracing to test.sh for debugging - Fixed tests to work with new implementation The server now works entirely with Go code, no git commands needed. Clone operations work correctly, but pull operations still have protocol negotiation issues that need further debugging. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
30ca57dae3
commit
8919cd5826
10 changed files with 124 additions and 105 deletions
|
|
@ -5,12 +5,14 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/chainguard-dev/clog"
|
||||
"github.com/imjasonh/infinite-git/internal/pktline"
|
||||
"github.com/imjasonh/infinite-git/internal/protocol"
|
||||
)
|
||||
|
||||
// handleInfoRefs handles the reference discovery phase.
|
||||
func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
|
||||
log := clog.FromContext(r.Context())
|
||||
service := r.URL.Query().Get("service")
|
||||
|
||||
// Only support git-upload-pack (fetch/clone)
|
||||
|
|
@ -25,12 +27,12 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
|
|||
s.mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("failed to generate commit", "error", err)
|
||||
log.Error("failed to generate commit", "error", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("generated new commit", "sha", commitSHA, "counter", s.generator.GetCounter())
|
||||
log.Info("generated new commit", "sha", commitSHA, "counter", s.generator.GetCounter())
|
||||
|
||||
// Set headers
|
||||
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", service))
|
||||
|
|
@ -41,18 +43,18 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Service declaration
|
||||
if err := pw.Writef("# service=%s\n", service); err != nil {
|
||||
s.logger.Error("failed to write service line", "error", err)
|
||||
log.Error("failed to write service line", "error", err)
|
||||
return
|
||||
}
|
||||
if err := pw.Flush(); err != nil {
|
||||
s.logger.Error("failed to write flush", "error", err)
|
||||
log.Error("failed to write flush", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get current refs
|
||||
refs, err := s.repo.GetRefs()
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get refs", "error", err)
|
||||
log.Error("failed to get refs", "error", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
@ -64,13 +66,13 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
|
|||
for ref, oid := range refs {
|
||||
if first {
|
||||
if err := pw.Writef("%s %s\x00%s\n", oid, ref, capabilities); err != nil {
|
||||
s.logger.Error("failed to write ref with capabilities", "error", err)
|
||||
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 {
|
||||
s.logger.Error("failed to write ref", "error", err)
|
||||
log.Error("failed to write ref", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -78,13 +80,14 @@ func (s *Server) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Final flush
|
||||
if err := pw.Flush(); err != nil {
|
||||
s.logger.Error("failed to write final flush", "error", err)
|
||||
log.Error("failed to write final flush", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handleUploadPack handles the pack upload phase.
|
||||
func (s *Server) handleUploadPack(w http.ResponseWriter, r *http.Request) {
|
||||
log := clog.FromContext(r.Context())
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
|
|
@ -99,10 +102,10 @@ func (s *Server) handleUploadPack(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Process the request
|
||||
if err := up.HandleRequest(r.Body, w); err != nil {
|
||||
s.logger.Error("upload-pack failed", "error", err)
|
||||
log.Error("upload-pack failed", "error", err)
|
||||
// Don't send HTTP error here as we may have already started writing response
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("completed upload-pack")
|
||||
log.Info("completed upload-pack")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/chainguard-dev/clog"
|
||||
"github.com/imjasonh/infinite-git/internal/generator"
|
||||
"github.com/imjasonh/infinite-git/internal/repo"
|
||||
)
|
||||
|
|
@ -14,15 +14,13 @@ type Server struct {
|
|||
repo *repo.Repository
|
||||
generator *generator.Generator
|
||||
mu sync.Mutex
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// New creates a new Git HTTP server.
|
||||
func New(r *repo.Repository, logger *slog.Logger) *Server {
|
||||
func New(r *repo.Repository) *Server {
|
||||
return &Server{
|
||||
repo: r,
|
||||
generator: generator.New(r),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +42,8 @@ func (s *Server) Handler() http.Handler {
|
|||
// logMiddleware logs HTTP requests.
|
||||
func (s *Server) logMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
s.logger.Info("request",
|
||||
log := clog.FromContext(r.Context())
|
||||
log.Info("request",
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"query", r.URL.RawQuery,
|
||||
|
|
@ -56,7 +55,8 @@ func (s *Server) logMiddleware(next http.Handler) http.Handler {
|
|||
|
||||
// handleReceivePack rejects push operations.
|
||||
func (s *Server) handleReceivePack(w http.ResponseWriter, r *http.Request) {
|
||||
s.logger.Info("rejecting push attempt", "path", r.URL.Path)
|
||||
log := clog.FromContext(r.Context())
|
||||
log.Info("rejecting push attempt", "path", r.URL.Path)
|
||||
http.Error(w, "Push access denied", http.StatusForbidden)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue