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
|
|