Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

Goit/src/goit/git.go (377 lines, 8.0 KiB) -rw-r--r-- file download

e08e053 Jakob Wakeling 2023-07-18 21:31:14
0
// Copyright (C) 2023, Jakob Wakeling
e08e053 Jakob Wakeling 2023-07-18 21:31:14
1
// All rights reserved.
e08e053 Jakob Wakeling 2023-07-18 21:31:14
2
e08e053 Jakob Wakeling 2023-07-18 21:31:14
3
package goit
e08e053 Jakob Wakeling 2023-07-18 21:31:14
4
e08e053 Jakob Wakeling 2023-07-18 21:31:14
5
import (
e08e053 Jakob Wakeling 2023-07-18 21:31:14
6
	"bytes"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
7
	"compress/gzip"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
8
	"io"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
9
	"log"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
10
	"net/http"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
11
	"os"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
12
	"os/exec"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
13
	"strconv"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
14
	"strings"
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
15
	"sync"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
16
3f4c3f4 Jakob Wakeling 2023-12-01 23:26:03
17
	"github.com/go-chi/chi/v5"
54be405 Jakob Wakeling 2023-12-17 20:18:15
18
	"github.com/go-git/go-git/v5/plumbing"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
19
	"github.com/go-git/go-git/v5/plumbing/format/diff"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
20
	"github.com/go-git/go-git/v5/plumbing/object"
e08e053 Jakob Wakeling 2023-07-18 21:31:14
21
)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
22
6727af8 Jakob Wakeling 2023-07-19 23:43:37
23
type gitCommand struct {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
24
	prog string
e08e053 Jakob Wakeling 2023-07-18 21:31:14
25
	args []string
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
26
	Dir  string
e08e053 Jakob Wakeling 2023-07-18 21:31:14
27
	env  []string
e08e053 Jakob Wakeling 2023-07-18 21:31:14
28
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
29
e08e053 Jakob Wakeling 2023-07-18 21:31:14
30
func HandleInfoRefs(w http.ResponseWriter, r *http.Request) {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
31
	service := r.FormValue("service")
1828e5f Jakob Wakeling 2023-07-19 19:53:59
32
6727af8 Jakob Wakeling 2023-07-19 23:43:37
33
	repo := gitHttpBase(w, r, service)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
34
	if repo == nil {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
35
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
36
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
37
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
38
	c := NewGitCommand(strings.TrimPrefix(service, "git-"), "--stateless-rpc", "--advertise-refs", ".")
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
39
	c.AddEnv(os.Environ()...)
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
40
	c.AddEnv("GIT_PROTOCOL=version=2")
08dff9a Jakob Wakeling 2023-11-07 21:03:04
41
	c.Dir = RepoPath(repo.Name, true)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
42
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
43
	refs, _, err := c.Run(nil, nil)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
44
	if err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
45
		log.Println("[Git HTTP]", err.Error())
8abd343 Jakob Wakeling 2023-07-19 00:40:45
46
		HttpError(w, http.StatusInternalServerError)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
47
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
48
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
49
e08e053 Jakob Wakeling 2023-07-18 21:31:14
50
	w.Header().Add("Content-Type", "application/x-"+service+"-advertisement")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
51
	w.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
52
	w.Header().Set("Pragma", "no-cache")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
53
	w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
54
	w.WriteHeader(http.StatusOK)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
55
e08e053 Jakob Wakeling 2023-07-18 21:31:14
56
	w.Write(pktLine("# service=" + service + "\n"))
e08e053 Jakob Wakeling 2023-07-18 21:31:14
57
	w.Write(pktFlush())
e08e053 Jakob Wakeling 2023-07-18 21:31:14
58
	w.Write(refs)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
59
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
60
e08e053 Jakob Wakeling 2023-07-18 21:31:14
61
func HandleUploadPack(w http.ResponseWriter, r *http.Request) {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
62
	const service = "git-upload-pack"
1828e5f Jakob Wakeling 2023-07-19 19:53:59
63
6727af8 Jakob Wakeling 2023-07-19 23:43:37
64
	repo := gitHttpBase(w, r, service)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
65
	if repo == nil {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
66
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
67
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
68
6727af8 Jakob Wakeling 2023-07-19 23:43:37
69
	gitHttpRpc(w, r, service, repo)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
70
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
71
e08e053 Jakob Wakeling 2023-07-18 21:31:14
72
func HandleReceivePack(w http.ResponseWriter, r *http.Request) {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
73
	const service = "git-receive-pack"
6727af8 Jakob Wakeling 2023-07-19 23:43:37
74
6727af8 Jakob Wakeling 2023-07-19 23:43:37
75
	repo := gitHttpBase(w, r, service)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
76
	if repo == nil {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
77
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
78
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
79
6727af8 Jakob Wakeling 2023-07-19 23:43:37
80
	gitHttpRpc(w, r, service, repo)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
81
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
82
6727af8 Jakob Wakeling 2023-07-19 23:43:37
83
func gitHttpBase(w http.ResponseWriter, r *http.Request, service string) *Repo {
3f4c3f4 Jakob Wakeling 2023-12-01 23:26:03
84
	reponame := chi.URLParam(r, "repo")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
85
6727af8 Jakob Wakeling 2023-07-19 23:43:37
86
	/* Check that the Git service and protocol version are supported */
6727af8 Jakob Wakeling 2023-07-19 23:43:37
87
	if service != "git-upload-pack" && service != "git-receive-pack" {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
88
		w.WriteHeader(http.StatusForbidden)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
89
		return nil
6727af8 Jakob Wakeling 2023-07-19 23:43:37
90
	}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
91
	if service == "git-upload-pack" && r.Header.Get("Git-Protocol") != "version=2" {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
92
		w.WriteHeader(http.StatusForbidden)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
93
		return nil
e08e053 Jakob Wakeling 2023-07-18 21:31:14
94
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
95
6727af8 Jakob Wakeling 2023-07-19 23:43:37
96
	/* Load the repository from the database */
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
97
	repo, err := GetRepoByName(reponame)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
98
	if err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
99
		log.Println("[Git HTTP]", err.Error())
6727af8 Jakob Wakeling 2023-07-19 23:43:37
100
		w.WriteHeader(http.StatusInternalServerError)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
101
		return nil
e08e053 Jakob Wakeling 2023-07-18 21:31:14
102
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
103
e08e053 Jakob Wakeling 2023-07-18 21:31:14
104
	/* Require authentication other than for public pull */
778d467 Jakob Wakeling 2024-01-17 18:29:55
105
	if repo == nil || repo.Visibility != Public || service == "git-receive-pack" {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
106
		username, password, ok := r.BasicAuth()
6727af8 Jakob Wakeling 2023-07-19 23:43:37
107
		if !ok {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
108
			w.Header().Set("WWW-Authenticate", "Basic realm=\"git\"")
6727af8 Jakob Wakeling 2023-07-19 23:43:37
109
			w.WriteHeader(http.StatusUnauthorized)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
110
			return nil
6727af8 Jakob Wakeling 2023-07-19 23:43:37
111
		}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
112
6727af8 Jakob Wakeling 2023-07-19 23:43:37
113
		user, err := GetUserByName(username)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
114
		if err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
115
			log.Println("[Git HTTP]", err.Error())
6727af8 Jakob Wakeling 2023-07-19 23:43:37
116
			w.WriteHeader(http.StatusInternalServerError)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
117
			return nil
6727af8 Jakob Wakeling 2023-07-19 23:43:37
118
		}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
119
6727af8 Jakob Wakeling 2023-07-19 23:43:37
120
		/* If the user doesn't exist or has invalid credentials */
6727af8 Jakob Wakeling 2023-07-19 23:43:37
121
		if user == nil || !bytes.Equal(Hash(password, user.Salt), user.Pass) {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
122
			w.Header().Set("WWW-Authenticate", "Basic realm=\"git\"")
6727af8 Jakob Wakeling 2023-07-19 23:43:37
123
			w.WriteHeader(http.StatusUnauthorized)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
124
			return nil
6727af8 Jakob Wakeling 2023-07-19 23:43:37
125
		}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
126
778d467 Jakob Wakeling 2024-01-17 18:29:55
127
		/* If the repo doesn't exist or is private and not owned by the user */
778d467 Jakob Wakeling 2024-01-17 18:29:55
128
		if repo == nil || (repo.Visibility == Private && user.Id != repo.OwnerId) {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
129
			w.WriteHeader(http.StatusNotFound)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
130
			return nil
6727af8 Jakob Wakeling 2023-07-19 23:43:37
131
		}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
132
	}
6727af8 Jakob Wakeling 2023-07-19 23:43:37
133
6727af8 Jakob Wakeling 2023-07-19 23:43:37
134
	if repo == nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
135
		w.WriteHeader(http.StatusNotFound)
6727af8 Jakob Wakeling 2023-07-19 23:43:37
136
		return nil
e08e053 Jakob Wakeling 2023-07-18 21:31:14
137
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
138
e08e053 Jakob Wakeling 2023-07-18 21:31:14
139
	return repo
e08e053 Jakob Wakeling 2023-07-18 21:31:14
140
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
141
6727af8 Jakob Wakeling 2023-07-19 23:43:37
142
func gitHttpRpc(w http.ResponseWriter, r *http.Request, service string, repo *Repo) {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
143
	defer func() {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
144
		if err := r.Body.Close(); err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
145
			log.Println("[Git RPC]", err.Error())
e08e053 Jakob Wakeling 2023-07-18 21:31:14
146
		}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
147
	}()
e08e053 Jakob Wakeling 2023-07-18 21:31:14
148
e08e053 Jakob Wakeling 2023-07-18 21:31:14
149
	if r.Header.Get("Content-Type") != "application/x-"+service+"-request" {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
150
		log.Println("[Git RPC]", "Content-Type mismatch")
8abd343 Jakob Wakeling 2023-07-19 00:40:45
151
		HttpError(w, http.StatusUnauthorized)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
152
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
153
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
154
e08e053 Jakob Wakeling 2023-07-18 21:31:14
155
	body := r.Body
e08e053 Jakob Wakeling 2023-07-18 21:31:14
156
	if r.Header.Get("Content-Encoding") == "gzip" {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
157
		if b, err := gzip.NewReader(r.Body); err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
158
			log.Println("[Git RPC]", err.Error())
8abd343 Jakob Wakeling 2023-07-19 00:40:45
159
			HttpError(w, http.StatusInternalServerError)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
160
			return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
161
		} else {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
162
			body = b
e08e053 Jakob Wakeling 2023-07-18 21:31:14
163
		}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
164
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
165
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
166
	c := NewGitCommand(strings.TrimPrefix(service, "git-"), "--stateless-rpc", ".")
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
167
	c.AddEnv(os.Environ()...)
08dff9a Jakob Wakeling 2023-11-07 21:03:04
168
	c.Dir = RepoPath(repo.Name, true)
1828e5f Jakob Wakeling 2023-07-19 19:53:59
169
1828e5f Jakob Wakeling 2023-07-19 19:53:59
170
	if p := r.Header.Get("Git-Protocol"); p == "version=2" {
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
171
		c.AddEnv("GIT_PROTOCOL=version=2")
1828e5f Jakob Wakeling 2023-07-19 19:53:59
172
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
173
e08e053 Jakob Wakeling 2023-07-18 21:31:14
174
	w.Header().Add("Content-Type", "application/x-"+service+"-result")
e08e053 Jakob Wakeling 2023-07-18 21:31:14
175
	w.WriteHeader(http.StatusOK)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
176
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
177
	if _, _, err := c.Run(body, w); err != nil {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
178
		log.Println("[Git RPC]", err.Error())
8abd343 Jakob Wakeling 2023-07-19 00:40:45
179
		HttpError(w, http.StatusInternalServerError)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
180
		return
e08e053 Jakob Wakeling 2023-07-18 21:31:14
181
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
182
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
183
e08e053 Jakob Wakeling 2023-07-18 21:31:14
184
func pktLine(str string) []byte {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
185
	s := strconv.FormatUint(uint64(len(str)+4), 16)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
186
	s = strings.Repeat("0", 4-len(s)%4) + s
e08e053 Jakob Wakeling 2023-07-18 21:31:14
187
	return []byte(s + str)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
188
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
189
e08e053 Jakob Wakeling 2023-07-18 21:31:14
190
func pktFlush() []byte { return []byte("0000") }
e08e053 Jakob Wakeling 2023-07-18 21:31:14
191
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
192
func NewGitCommand(args ...string) *gitCommand {
6727af8 Jakob Wakeling 2023-07-19 23:43:37
193
	return &gitCommand{prog: "git", args: args}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
194
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
195
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
196
func (C *gitCommand) AddEnv(env ...string) {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
197
	C.env = append(C.env, env...)
e08e053 Jakob Wakeling 2023-07-18 21:31:14
198
}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
199
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
200
func (C *gitCommand) Run(in io.Reader, out io.Writer) ([]byte, []byte, error) {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
201
	c := exec.Command(C.prog, C.args...)
3e159d3 Jakob Wakeling 2023-08-06 23:29:48
202
	c.Dir = C.Dir
e08e053 Jakob Wakeling 2023-07-18 21:31:14
203
	c.Env = C.env
e08e053 Jakob Wakeling 2023-07-18 21:31:14
204
	c.Stdin = in
e08e053 Jakob Wakeling 2023-07-18 21:31:14
205
e08e053 Jakob Wakeling 2023-07-18 21:31:14
206
	stdout := &bytes.Buffer{}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
207
	stderr := &bytes.Buffer{}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
208
	c.Stdout = stdout
e08e053 Jakob Wakeling 2023-07-18 21:31:14
209
	c.Stderr = os.Stderr
e08e053 Jakob Wakeling 2023-07-18 21:31:14
210
e08e053 Jakob Wakeling 2023-07-18 21:31:14
211
	if out != nil {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
212
		c.Stdout = out
e08e053 Jakob Wakeling 2023-07-18 21:31:14
213
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
214
e08e053 Jakob Wakeling 2023-07-18 21:31:14
215
	if err := c.Run(); err != nil {
e08e053 Jakob Wakeling 2023-07-18 21:31:14
216
		return nil, stderr.Bytes(), err
e08e053 Jakob Wakeling 2023-07-18 21:31:14
217
	}
e08e053 Jakob Wakeling 2023-07-18 21:31:14
218
e08e053 Jakob Wakeling 2023-07-18 21:31:14
219
	return stdout.Bytes(), stderr.Bytes(), nil
e08e053 Jakob Wakeling 2023-07-18 21:31:14
220
}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
221
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
222
type DiffStat struct {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
223
	Name, Prev string
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
224
	Status     string
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
225
	Addition   int
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
226
	Deletion   int
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
227
	IsBinary   bool
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
228
}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
229
54be405 Jakob Wakeling 2023-12-17 20:18:15
230
var diffs = map[plumbing.Hash][]DiffStat{}
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
231
var diffsLock sync.RWMutex
54be405 Jakob Wakeling 2023-12-17 20:18:15
232
313c62b Jakob Wakeling 2023-12-24 00:08:13
233
var Sizes = map[plumbing.Hash]uint64{}
313c62b Jakob Wakeling 2023-12-24 00:08:13
234
var SizesLock sync.RWMutex
313c62b Jakob Wakeling 2023-12-24 00:08:13
235
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
236
func DiffStats(c *object.Commit) ([]DiffStat, error) {
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
237
	diffsLock.RLock()
54be405 Jakob Wakeling 2023-12-17 20:18:15
238
	if stats, ok := diffs[c.Hash]; ok {
dabb967 Jakob Wakeling 2023-12-19 00:45:39
239
		diffsLock.RUnlock()
54be405 Jakob Wakeling 2023-12-17 20:18:15
240
		return stats, nil
54be405 Jakob Wakeling 2023-12-17 20:18:15
241
	}
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
242
	diffsLock.RUnlock()
54be405 Jakob Wakeling 2023-12-17 20:18:15
243
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
244
	from, err := c.Tree()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
245
	if err != nil {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
246
		return nil, err
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
247
	}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
248
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
249
	to := &object.Tree{}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
250
	if c.NumParents() != 0 {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
251
		parent, err := c.Parents().Next()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
252
		if err != nil {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
253
			return nil, err
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
254
		}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
255
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
256
		to, err = parent.Tree()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
257
		if err != nil {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
258
			return nil, err
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
259
		}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
260
	}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
261
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
262
	patch, err := to.Patch(from)
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
263
	if err != nil {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
264
		return nil, err
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
265
	}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
266
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
267
	var stats []DiffStat
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
268
	for _, fp := range patch.FilePatches() {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
269
		var stat DiffStat
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
270
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
271
		if len(fp.Chunks()) == 0 {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
272
			if !fp.IsBinary() {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
273
				continue
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
274
			}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
275
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
276
			stat.IsBinary = true
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
277
		}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
278
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
279
		from, to := fp.Files()
6b13ddb Jakob Wakeling 2023-12-17 13:42:14
280
		if from == nil && to == nil {
6b13ddb Jakob Wakeling 2023-12-17 13:42:14
281
			continue
6b13ddb Jakob Wakeling 2023-12-17 13:42:14
282
		} else if from == nil /* Added */ {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
283
			stat.Name = to.Path()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
284
			stat.Status = "A"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
285
		} else if to == nil /* Deleted */ {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
286
			stat.Name = from.Path()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
287
			stat.Status = "D"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
288
		} else if from.Path() != to.Path() /* Renamed */ {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
289
			stat.Name = to.Path()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
290
			stat.Prev = from.Path()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
291
			stat.Status = "R"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
292
		} else {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
293
			stat.Name = from.Path()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
294
			stat.Status = "M"
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
295
		}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
296
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
297
		for _, chunk := range fp.Chunks() {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
298
			s := chunk.Content()
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
299
			if len(s) == 0 {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
300
				continue
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
301
			}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
302
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
303
			switch chunk.Type() {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
304
			case diff.Add:
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
305
				stat.Addition += strings.Count(s, "\n")
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
306
				if s[len(s)-1] != '\n' {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
307
					stat.Addition++
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
308
				}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
309
			case diff.Delete:
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
310
				stat.Deletion += strings.Count(s, "\n")
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
311
				if s[len(s)-1] != '\n' {
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
312
					stat.Deletion++
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
313
				}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
314
			}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
315
		}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
316
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
317
		stats = append(stats, stat)
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
318
	}
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
319
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
320
	diffsLock.Lock()
54be405 Jakob Wakeling 2023-12-17 20:18:15
321
	diffs[c.Hash] = stats
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
322
	diffsLock.Unlock()
8ac6938 Jakob Wakeling 2023-12-18 15:10:30
323
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
324
	return stats, nil
4a22b95 Jakob Wakeling 2023-08-14 20:57:25
325
}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
326
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
327
type countPair struct {
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
328
	hash  plumbing.Hash
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
329
	count uint64
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
330
}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
331
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
332
var counts = map[string]countPair{}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
333
var countsLock sync.RWMutex
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
334
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
335
func CommitCount(repo, branch string, hash plumbing.Hash) (uint64, error) {
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
336
	countsLock.RLock()
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
337
	if count, ok := counts[repo+"/"+branch]; ok && count.hash == hash {
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
338
		countsLock.RUnlock()
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
339
		return count.count, nil
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
340
	}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
341
	countsLock.RUnlock()
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
342
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
343
	c := NewGitCommand("rev-list", "--count", branch)
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
344
	c.Dir = RepoPath(repo, true)
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
345
	out, _, err := c.Run(nil, nil)
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
346
	if err != nil {
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
347
		return 0, err
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
348
	}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
349
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
350
	count, err := strconv.ParseUint(strings.TrimSpace(string(out)), 10, 64)
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
351
	if err != nil {
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
352
		return 0, err
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
353
	}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
354
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
355
	countsLock.Lock()
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
356
	counts[repo+"/"+branch] = countPair{hash, count}
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
357
	countsLock.Unlock()
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
358
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
359
	return count, nil
d6784d5 Jakob Wakeling 2023-12-25 23:47:47
360
}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
361
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
362
func GetFileType(file *object.File) (string, error) {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
363
	rc, err := file.Blob.Reader()
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
364
	if err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
365
		return "", err
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
366
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
367
	defer rc.Close()
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
368
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
369
	buf := make([]byte, min(file.Size, 512))
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
370
	if _, err := rc.Read(buf); err != nil {
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
371
		return "", err
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
372
	}
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
373
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
374
	return http.DetectContentType(buf), nil
a0ac27c Jakob Wakeling 2024-07-06 23:32:05
375
}
376