parent:
d3acd9b203379c6a7170410e26a950ada40e8e20
nmyk <nick@nmyk.io>
2026-02-16T13:46:27-05:00
include tags
diff --git a/main.go b/main.go
index 6cf63e81a53a786e3527b000f6f56a583f722ed5..dd2b8362d9ad56026e91aec6f22cff4c7bb00e0f 100644
--- a/main.go
+++ b/main.go
@@ -38,6 +38,7 @@ mux := http.NewServeMux()
mux.HandleFunc("GET /", listRepos)
mux.HandleFunc("GET /{name}", repoIndex)
mux.HandleFunc("GET /{name}/refs", refs)
+ mux.HandleFunc("GET /{name}/refs/{type}", refs)
mux.HandleFunc("GET /{name}/tree/{ref}", repoTree)
mux.HandleFunc("GET /{name}/tree/{ref}/{path...}", repoTree)
mux.HandleFunc("GET /{name}/blob/{ref}/{path...}", blob)
@@ -99,9 +100,15 @@ type BranchMeta struct {
Name string
}
+type TagMeta struct {
+ Name string
+}
+
type RefsData struct {
Repo string
+ Type string
Branches []BranchMeta
+ Tags []TagMeta
}
func refs(w http.ResponseWriter, r *http.Request) {
@@ -115,21 +122,39 @@ if err != nil {
http.Error(w, err.Error(), 500)
return
}
- iter, err := repo.Branches()
+ branchIter, err := repo.Branches()
if err != nil {
http.Error(w, err.Error(), 500)
}
var branches []BranchMeta
- err = iter.ForEach(func(ref *plumbing.Reference) error {
+ err = branchIter.ForEach(func(ref *plumbing.Reference) error {
branches = append(branches, BranchMeta{ref.Name().Short()})
return nil
})
if err != nil {
http.Error(w, err.Error(), 500)
}
+ tagIter, err := repo.Tags()
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ }
+ var tags []TagMeta
+ err = tagIter.ForEach(func(ref *plumbing.Reference) error {
+ tags = append(tags, TagMeta{ref.Name().Short()})
+ return nil
+ })
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ }
+ refType := r.PathValue("type")
+ if !slices.Contains([]string{"branches", "tags"}, refType) {
+ refType = "branches"
+ }
data := RefsData{
Repo: repoName,
+ Type: refType,
Branches: branches,
+ Tags: tags,
}
serve(w, "refs", data)
}
@@ -146,8 +171,7 @@ http.Error(w, err.Error(), 500)
return
}
refName := r.PathValue("ref")
- branch := path.Join("refs", "heads", refName)
- ref, err := repo.Reference(plumbing.ReferenceName(branch), true)
+ ref, err := resolve(repo, refName)
if err != nil {
http.NotFound(w, r)
return
@@ -205,6 +229,28 @@ Path string
IsDir bool
}
+func resolve(repo *git.Repository, refName string) (*plumbing.Reference, error) {
+ branchRef := plumbing.ReferenceName(path.Join("refs", "heads", refName))
+ ref, err := repo.Reference(branchRef, true)
+ if err == nil {
+ return ref, nil
+ }
+ tagRef := plumbing.ReferenceName(path.Join("refs", "tags", refName))
+ ref, err = repo.Reference(tagRef, true)
+ if err != nil {
+ return nil, err
+ }
+ // Resolve annotated tag to commit hash
+ obj, err := repo.Object(plumbing.AnyObject, ref.Hash())
+ if err != nil {
+ return nil, err
+ }
+ if tagObj, ok := obj.(*object.Tag); ok {
+ ref = plumbing.NewHashReference(ref.Name(), tagObj.Target)
+ }
+ return ref, nil
+}
+
func repoTree(w http.ResponseWriter, r *http.Request) {
repoName := r.PathValue("name")
if _, err := os.Stat(repoName + DOT_GIT); os.IsNotExist(err) {
@@ -227,8 +273,7 @@ if refName == headRef && treePath == "" {
repoIndex(w, r)
return
}
- branch := path.Join("refs", "heads", refName)
- ref, err := repo.Reference(plumbing.ReferenceName(branch), true)
+ ref, err := resolve(repo, refName)
if err != nil {
http.NotFound(w, r)
return
diff --git a/templates/refs.tmpl b/templates/refs.tmpl
index 69cce1f4c73c8d5c7fcec8b65f7bc774312af99c..1a2d878e194b02853a3df4d462c39a70a830bf8d 100644
--- a/templates/refs.tmpl
+++ b/templates/refs.tmpl
@@ -1,11 +1,28 @@
{{define "title"}}{{.Repo}}{{end}}
{{define "content"}}
<h1><a href="/{{.Repo}}">{{.Repo}}</a></h1>
-<h2>branches</h2>
+
+<h2>
+ {{if and (eq .Type "branches") (gt (len .Tags) 0)}}
+ branches | <a href="/{{.Repo}}/refs/tags">tags</a>
+ {{else if eq .Type "branches"}}
+ branches
+ {{else if eq .Type "tags"}}
+ <a href="/{{.Repo}}/refs/branches">branches</a> | tags
+ {{end}}
+</h2>
+
<ul>
-{{range .Branches}}
-<li><a href="/{{$.Repo}}/tree/{{.Name}}">{{.Name}}</a></li>
-{{end}}
+ {{if eq .Type "branches"}}
+ {{range .Branches}}
+ <li><a href="/{{$.Repo}}/tree/{{.Name}}">{{.Name}}</a></li>
+ {{end}}
+ {{else if eq .Type "tags"}}
+ {{range .Tags}}
+ <li><a href="/{{$.Repo}}/tree/{{.Name}}">{{.Name}}</a></li>
+ {{end}}
+ {{else}}
+ {{end}}
</ul>
{{end}}