all repos

mugit @ a00560c

🐮 git server that your cow will love
2 files changed, 86 insertions(+), 32 deletions(-)
refactor git
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-01-21 01:49:14 +0200
Change ID: wkuwunxpuylrrwmzwrvmltsxtkszkrnt
Parent: 70e0353
M internal/git/gitservice/gitservice.go

@@ -5,11 +5,9 @@ "bytes"

"fmt" "io" "os/exec" - "strings" + "sync" "syscall" ) - -// Thanks https://git.icyphox.sh/legit/blob/master/git/service/service.go // InfoRefs executes git-upload-pack --advertise-refs for smart-HTTP discovery. func InfoRefs(dir string, out io.Writer) error {

@@ -20,9 +18,12 @@ "--advertise-refs",

".") cmd.Dir = dir cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - stdoutPipe, _ := cmd.StdoutPipe() + + stdout, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("stdout pipe: %w", err) + } cmd.Stderr = cmd.Stdout - defer stdoutPipe.Close() if err := cmd.Start(); err != nil { return fmt.Errorf("start git-upload-pack: %w", err)

@@ -31,20 +32,17 @@

if err := packLine(out, "# service=git-upload-pack\n"); err != nil { return fmt.Errorf("write pack line: %w", err) } - if err := packFlush(out); err != nil { return fmt.Errorf("flush pack: %w", err) } var buf bytes.Buffer - if _, err := io.Copy(&buf, stdoutPipe); err != nil { + if _, err := io.Copy(&buf, stdout); err != nil { return fmt.Errorf("copy stdout to buffer: %w", err) } if err := cmd.Wait(); err != nil { - var out strings.Builder - io.Copy(&out, &buf) - return fmt.Errorf("git-upload-pack: %w: %s", err, out.String()) + return fmt.Errorf("git-upload-pack: %w", err) } if _, err := io.Copy(out, &buf); err != nil {

@@ -55,42 +53,99 @@ return nil

} // UploadPack executes git-upload-pack for smart-HTTP git fetch/clone. -func UploadPack(dir string, in io.Reader, out io.Writer) error { - cmd := exec.Command("git", - "-c", "uploadpack.allowFilter=true", - "upload-pack", - "--stateless-rpc", - ".") - cmd.Dir = dir +// StatelessRPC should be true in case it's used over http, and false for ssh. +func UploadPack(dir string, statelessRPC bool, in io.Reader, out io.Writer) error { + return gitCmd("upload-pack", config{ + Dir: dir, + StatelessRPC: statelessRPC, + AllowFilter: true, + Stdin: in, + Stdout: out, + }) +} + +func ReceivePack(dir string, in io.Reader, out io.Writer) error { + return gitCmd("receive-pack", config{ + Dir: dir, + Stdin: in, + Stdout: out, + }) +} + +type config struct { + Dir string + StatelessRPC bool + AllowFilter bool + ExtraArgs []string + Stdin io.Reader + Stdout io.Writer +} + +func gitCmd(service string, c config) error { + args := []string{} + if c.AllowFilter { + args = append(args, "-c", "uploadpack.allowFilter=true") + } + + args = append(args, service) + if c.StatelessRPC { + args = append(args, "--stateless-rpc") + } + + args = append(args, c.ExtraArgs...) + args = append(args, ".") + + cmd := exec.Command("git", args...) + cmd.Dir = c.Dir cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - stdoutPipe, _ := cmd.StdoutPipe() - cmd.Stderr = cmd.Stdout - defer stdoutPipe.Close() + var ( + err error + stdin io.WriteCloser + ) + + if c.Stdin != nil { + stdin, err = cmd.StdinPipe() + if err != nil { + return err + } + } - stdinPipe, err := cmd.StdinPipe() + stdout, err := cmd.StdoutPipe() if err != nil { - return fmt.Errorf("stdin pipe: %w", err) + return err } - defer stdinPipe.Close() + cmd.Stderr = cmd.Stdout if err := cmd.Start(); err != nil { - return fmt.Errorf("start git-upload-pack: %w", err) + return fmt.Errorf("start %s: %w", service, err) } - if _, err := io.Copy(stdinPipe, in); err != nil { - return fmt.Errorf("copy to stdin: %w", err) + if c.Stdin != nil { + // Don't add to WaitGroup - stdin closes when client closes, + // shouldn't block waiting for output to finish + go func() { + defer stdin.Close() + io.Copy(stdin, c.Stdin) + }() } - stdinPipe.Close() + + var wg sync.WaitGroup + var stdoutErr error + + wg.Go(func() { + _, stdoutErr = io.Copy(c.Stdout, stdout) + }) - if _, err := io.Copy(out, stdoutPipe); err != nil { - return fmt.Errorf("copy stdout: %w", err) + wg.Wait() + + if stdoutErr != nil { + return fmt.Errorf("copy stdout: %w", stdoutErr) } if err := cmd.Wait(); err != nil { - return fmt.Errorf("git-upload-pack: %w", err) + return fmt.Errorf("%s: %w", service, err) } - return nil }
M internal/handlers/git.go

@@ -20,7 +20,6 @@ w.WriteHeader(http.StatusOK)

if err := gitservice.InfoRefs( filepath.Join(h.c.Repo.Dir, name), // FIXME: use securejoin - true, w, ); err != nil { slog.Error("git: info/refs", "err", err)