bidet

commit e4c00fc2a01331bb0b70d8f881ce63a4d7dbccbf

tree

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