+1
appview/pages/pages.go
+1
appview/pages/pages.go
+12
-9
appview/repo/index.go
+12
-9
appview/repo/index.go
···
24
24
25
25
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
26
26
ref := chi.URLParam(r, "ref")
27
+
27
28
f, err := rp.repoResolver.Resolve(r)
28
29
if err != nil {
29
30
log.Println("failed to fully resolve repo", err)
···
118
119
119
120
var forkInfo *types.ForkInfo
120
121
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
121
-
forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient)
122
+
forkInfo, err = getForkInfo(repoInfo, rp, f, result.Ref, user, signedClient)
122
123
if err != nil {
123
124
log.Printf("Failed to fetch fork information: %v", err)
124
125
return
···
126
127
}
127
128
128
129
// TODO: a bit dirty
129
-
languageInfo, err := rp.getLanguageInfo(f, signedClient, chi.URLParam(r, "ref") == "")
130
+
languageInfo, err := rp.getLanguageInfo(f, signedClient, result.Ref, ref == "")
130
131
if err != nil {
131
132
log.Printf("failed to compute language percentages: %s", err)
132
133
// non-fatal
···
161
162
func (rp *Repo) getLanguageInfo(
162
163
f *reporesolver.ResolvedRepo,
163
164
signedClient *knotclient.SignedClient,
165
+
currentRef string,
164
166
isDefaultRef bool,
165
167
) ([]types.RepoLanguageDetails, error) {
166
168
// first attempt to fetch from db
167
169
langs, err := db.GetRepoLanguages(
168
170
rp.db,
169
171
db.FilterEq("repo_at", f.RepoAt()),
170
-
db.FilterEq("ref", f.Ref),
172
+
db.FilterEq("ref", currentRef),
171
173
)
172
174
173
175
if err != nil || langs == nil {
174
176
// non-fatal, fetch langs from ks
175
-
ls, err := signedClient.RepoLanguages(f.OwnerDid(), f.Name, f.Ref)
177
+
ls, err := signedClient.RepoLanguages(f.OwnerDid(), f.Name, currentRef)
176
178
if err != nil {
177
179
return nil, err
178
180
}
···
183
185
for l, s := range ls.Languages {
184
186
langs = append(langs, db.RepoLanguage{
185
187
RepoAt: f.RepoAt(),
186
-
Ref: f.Ref,
188
+
Ref: currentRef,
187
189
IsDefaultRef: isDefaultRef,
188
190
Language: l,
189
191
Bytes: s,
···
234
236
repoInfo repoinfo.RepoInfo,
235
237
rp *Repo,
236
238
f *reporesolver.ResolvedRepo,
239
+
currentRef string,
237
240
user *oauth.User,
238
241
signedClient *knotclient.SignedClient,
239
242
) (*types.ForkInfo, error) {
···
264
267
}
265
268
266
269
if !slices.ContainsFunc(result.Branches, func(branch types.Branch) bool {
267
-
return branch.Name == f.Ref
270
+
return branch.Name == currentRef
268
271
}) {
269
272
forkInfo.Status = types.MissingBranch
270
273
return &forkInfo, nil
271
274
}
272
275
273
-
newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref)
276
+
newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, currentRef, currentRef)
274
277
if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent {
275
278
log.Printf("failed to update tracking branch: %s", err)
276
279
return nil, err
277
280
}
278
281
279
-
hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
282
+
hiddenRef := fmt.Sprintf("hidden/%s/%s", currentRef, currentRef)
280
283
281
284
var status types.AncestorCheckResponse
282
-
forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt()), repoInfo.Name, f.Ref, hiddenRef)
285
+
forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt()), repoInfo.Name, currentRef, hiddenRef)
283
286
if err != nil {
284
287
log.Printf("failed to check if fork is ahead/behind: %s", err)
285
288
return nil, err
+3
-1
appview/repo/repo.go
+3
-1
appview/repo/repo.go
···
1315
1315
}
1316
1316
1317
1317
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
1318
+
ref := chi.URLParam(r, "ref")
1319
+
1318
1320
user := rp.oauth.GetUser(r)
1319
1321
f, err := rp.repoResolver.Resolve(r)
1320
1322
if err != nil {
···
1345
1347
forkName := fmt.Sprintf("%s", f.Name)
1346
1348
forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.Repo.Name)
1347
1349
1348
-
_, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref)
1350
+
_, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, ref)
1349
1351
if err != nil {
1350
1352
rp.pages.Notice(w, "repo", "Failed to sync repository fork.")
1351
1353
return
+18
-39
appview/reporesolver/resolver.go
+18
-39
appview/reporesolver/resolver.go
···
7
7
"fmt"
8
8
"log"
9
9
"net/http"
10
-
"net/url"
11
10
"path"
11
+
"regexp"
12
12
"strings"
13
13
14
14
"github.com/bluesky-social/indigo/atproto/identity"
···
26
26
27
27
type ResolvedRepo struct {
28
28
db.Repo
29
-
OwnerId identity.Identity
30
-
Ref string
31
-
CurrentDir string
29
+
OwnerId identity.Identity
30
+
CurrentDir string
31
+
Ref string
32
32
33
33
rr *RepoResolver
34
34
}
···
56
56
return nil, fmt.Errorf("malformed middleware")
57
57
}
58
58
59
+
currentDir := path.Dir(extractPathAfterRef(r.URL.EscapedPath()))
59
60
ref := chi.URLParam(r, "ref")
60
61
61
-
if ref == "" {
62
-
us, err := knotclient.NewUnsignedClient(repo.Knot, rr.config.Core.Dev)
63
-
if err != nil {
64
-
return nil, err
65
-
}
66
-
67
-
defaultBranch, err := us.DefaultBranch(id.DID.String(), repo.Name)
68
-
if err != nil {
69
-
return nil, err
70
-
}
71
-
72
-
ref = defaultBranch.Branch
73
-
}
74
-
75
-
currentDir := path.Dir(extractPathAfterRef(r.URL.EscapedPath(), ref))
76
-
77
62
return &ResolvedRepo{
78
63
Repo: *repo,
79
64
OwnerId: id,
80
-
Ref: ref,
81
65
CurrentDir: currentDir,
66
+
Ref: ref,
82
67
83
68
rr: rr,
84
69
}, nil
···
200
185
if err != nil {
201
186
log.Printf("failed to create unsigned client for %s: %v", knot, err)
202
187
} else {
203
-
result, err := us.Branches(f.OwnerDid(), f.Name)
204
-
if err != nil {
188
+
if result, err := us.Branches(f.OwnerDid(), f.Name); err != nil {
205
189
log.Printf("failed to get branches for %s/%s: %v", f.OwnerDid(), f.Name, err)
206
-
}
207
-
208
-
if len(result.Branches) == 0 {
190
+
} else if len(result.Branches) == 0 {
209
191
disableFork = true
210
192
}
211
193
}
···
216
198
Name: f.Name,
217
199
RepoAt: repoAt,
218
200
Description: f.Description,
219
-
Ref: f.Ref,
220
201
IsStarred: isStarred,
221
202
Knot: knot,
222
203
Spindle: f.Spindle,
···
228
209
},
229
210
DisableFork: disableFork,
230
211
CurrentDir: f.CurrentDir,
212
+
Ref: f.Ref,
231
213
}
232
214
233
215
if sourceRepo != nil {
···
251
233
// after the ref. for example:
252
234
//
253
235
// /@icyphox.sh/foorepo/blob/main/abc/xyz/ => abc/xyz/
254
-
func extractPathAfterRef(fullPath, ref string) string {
236
+
func extractPathAfterRef(fullPath string) string {
255
237
fullPath = strings.TrimPrefix(fullPath, "/")
256
238
257
-
ref = url.PathEscape(ref)
239
+
// match blob/, tree/, or raw/ followed by any ref and then a slash
240
+
//
241
+
// captures everything after the final slash
242
+
pattern := `(?:blob|tree|raw)/[^/]+/(.*)$`
258
243
259
-
prefixes := []string{
260
-
fmt.Sprintf("blob/%s/", ref),
261
-
fmt.Sprintf("tree/%s/", ref),
262
-
fmt.Sprintf("raw/%s/", ref),
263
-
}
244
+
re := regexp.MustCompile(pattern)
245
+
matches := re.FindStringSubmatch(fullPath)
264
246
265
-
for _, prefix := range prefixes {
266
-
idx := strings.Index(fullPath, prefix)
267
-
if idx != -1 {
268
-
return fullPath[idx+len(prefix):]
269
-
}
247
+
if len(matches) > 1 {
248
+
return matches[1]
270
249
}
271
250
272
251
return ""
-19
appview/state/profile.go
-19
appview/state/profile.go
···
89
89
log.Printf("failed to create profile timeline for %s: %s", ident.DID.String(), err)
90
90
}
91
91
92
-
var didsToResolve []string
93
-
for _, r := range collaboratingRepos {
94
-
didsToResolve = append(didsToResolve, r.Did)
95
-
}
96
-
for _, byMonth := range timeline.ByMonth {
97
-
for _, pe := range byMonth.PullEvents.Items {
98
-
didsToResolve = append(didsToResolve, pe.Repo.Did)
99
-
}
100
-
for _, ie := range byMonth.IssueEvents.Items {
101
-
didsToResolve = append(didsToResolve, ie.Metadata.Repo.Did)
102
-
}
103
-
for _, re := range byMonth.RepoEvents {
104
-
didsToResolve = append(didsToResolve, re.Repo.Did)
105
-
if re.Source != nil {
106
-
didsToResolve = append(didsToResolve, re.Source.Did)
107
-
}
108
-
}
109
-
}
110
-
111
92
followers, following, err := db.GetFollowerFollowingCount(s.db, ident.DID.String())
112
93
if err != nil {
113
94
log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err)
+4
-2
knotserver/git/fork.go
+4
-2
knotserver/git/fork.go
···
27
27
return nil
28
28
}
29
29
30
-
func (g *GitRepo) Sync(branch string) error {
30
+
func (g *GitRepo) Sync() error {
31
+
branch := g.h.String()
32
+
31
33
fetchOpts := &git.FetchOptions{
32
34
RefSpecs: []config.RefSpec{
33
-
config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/heads/%s", branch, branch)),
35
+
config.RefSpec("+" + branch + ":" + branch), // +refs/heads/master:refs/heads/master
34
36
},
35
37
}
36
38
+2
-2
knotserver/handler.go
+2
-2
knotserver/handler.go
···
142
142
r.Delete("/", h.RemoveRepo)
143
143
r.Route("/fork", func(r chi.Router) {
144
144
r.Post("/", h.RepoFork)
145
-
r.Post("/sync/{branch}", h.RepoForkSync)
146
-
r.Get("/sync/{branch}", h.RepoForkAheadBehind)
145
+
r.Post("/sync/*", h.RepoForkSync)
146
+
r.Get("/sync/*", h.RepoForkAheadBehind)
147
147
})
148
148
})
149
149
+4
-4
knotserver/routes.go
+4
-4
knotserver/routes.go
···
710
710
}
711
711
712
712
func (h *Handle) RepoForkAheadBehind(w http.ResponseWriter, r *http.Request) {
713
-
l := h.l.With("handler", "RepoForkSync")
713
+
l := h.l.With("handler", "RepoForkAheadBehind")
714
714
715
715
data := struct {
716
716
Did string `json:"did"`
···
845
845
name = filepath.Base(source)
846
846
}
847
847
848
-
branch := chi.URLParam(r, "branch")
848
+
branch := chi.URLParam(r, "*")
849
849
branch, _ = url.PathUnescape(branch)
850
850
851
851
relativeRepoPath := filepath.Join(did, name)
852
852
repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath)
853
853
854
-
gr, err := git.PlainOpen(repoPath)
854
+
gr, err := git.Open(repoPath, branch)
855
855
if err != nil {
856
856
log.Println(err)
857
857
notFound(w)
858
858
return
859
859
}
860
860
861
-
err = gr.Sync(branch)
861
+
err = gr.Sync()
862
862
if err != nil {
863
863
l.Error("error syncing repo fork", "error", err.Error())
864
864
writeError(w, err.Error(), http.StatusInternalServerError)