all repos

mugit @ d220163

🐮 git server that your cow will love

mugit/internal/git/external.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
git: set default branch; dont replay on list of default branches (#7)..., 2 months ago
1
package git
2
3
import (
4
	"cmp"
5
	"context"
6
	"errors"
7
	"fmt"
8
	"io"
9
	"os/exec"
10
	"strings"
11
	"syscall"
12
)
13
14
var gitEnv = []string{
15
	"GIT_CONFIG_GLOBAL=/dev/null",
16
	"GIT_CONFIG_SYSTEM=/dev/null",
17
}
18
19
type cmdOpts struct {
20
	Cmd         []string
21
	GitProtocol string
22
	Stdin       io.Reader
23
	Stdout      io.Writer
24
	Stderr      io.Writer
25
}
26
27
func (g *Repo) gitCmd(ctx context.Context, opts cmdOpts) error {
28
	opts.Cmd = append(opts.Cmd, ".")
29
	cmd := exec.CommandContext(ctx, "git", opts.Cmd...)
30
	cmd.Dir = g.path
31
	cmd.Env = append(gitEnv, fmt.Sprintf("GIT_PROTOCOL=%s", opts.GitProtocol))
32
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
33
	cmd.Stdin = cmp.Or[io.Reader](opts.Stdin, strings.NewReader(""))
34
	cmd.Stdout = cmp.Or(opts.Stdout, io.Discard)
35
	cmd.Stderr = cmp.Or(opts.Stderr, io.Discard)
36
	return cmd.Run()
37
}
38
39
func (g *Repo) runGitCmd(cmd string, args ...string) ([]byte, error) {
40
	var gitArgs []string
41
	gitArgs = append(gitArgs, cmd)
42
	gitArgs = append(gitArgs, args...)
43
	gitCmd := exec.Command("git", gitArgs...)
44
	gitCmd.Dir = g.path
45
	gitCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
46
	gitCmd.Env = gitEnv
47
48
	out, err := gitCmd.Output()
49
	if err != nil {
50
		if err, ok := errors.AsType[*exec.ExitError](err); ok {
51
			return nil, fmt.Errorf("%w, stderr: %s", err, err.Stderr)
52
		}
53
		return nil, err
54
	}
55
	return out, nil
56
}
57
58
func (g *Repo) streamingGitLog(ctx context.Context, extraArgs ...string) (io.ReadCloser, error) {
59
	args := []string{"log", g.h.String()}
60
	args = append(args, extraArgs...)
61
62
	cmd := exec.CommandContext(ctx, "git", args...)
63
	cmd.Dir = g.path
64
65
	stdout, err := cmd.StdoutPipe()
66
	if err != nil {
67
		return nil, err
68
	}
69
70
	if err := cmd.Start(); err != nil {
71
		return nil, err
72
	}
73
74
	return &processReader{
75
		Reader: stdout,
76
		cmd:    cmd,
77
		stdout: stdout,
78
	}, nil
79
}
80
81
// processReader wraps a reader and ensures the associated process is cleaned up
82
type processReader struct {
83
	io.Reader
84
	cmd    *exec.Cmd
85
	stdout io.ReadCloser
86
}
87
88
func (pr *processReader) Close() error {
89
	if err := pr.stdout.Close(); err != nil {
90
		return err
91
	}
92
	return pr.cmd.Wait()
93
}