New Sign in

fierj

Public

Tiny personal git forge

e1e14992b26f527cd37610ae2c60285bd60ad466
diff --git a/main.go b/main.go
index 87deb84..db05552 100644
--- a/main.go
+++ b/main.go
@@ -63,10 +63,10 @@ func main() {
 	mux.HandleFunc("GET /users", UsersGet(users, cfg.UsersPath, tmpl))
 	mux.HandleFunc("POST /users", UsersPost(users, cfg.UsersPath, tmpl))
 	mux.HandleFunc("GET /{repo}", Tree(cfg, tmpl))
-	mux.HandleFunc("GET /{repo}/tree/{ref}/{path...}", Tree(cfg, tmpl))
-	mux.HandleFunc("GET /{repo}/blob/{ref}/{path...}", Blob(cfg, tmpl))
-	mux.HandleFunc("GET /{repo}/log/{ref}", Log(cfg, tmpl))
-	mux.HandleFunc("GET /{repo}/commit/{ref}", Diff(cfg, tmpl))
+	mux.HandleFunc("GET /{repo}/tree/{refpath...}", Tree(cfg, tmpl))
+	mux.HandleFunc("GET /{repo}/blob/{refpath...}", Blob(cfg, tmpl))
+	mux.HandleFunc("GET /{repo}/log/{refpath...}", Log(cfg, tmpl))
+	mux.HandleFunc("GET /{repo}/commit/{hash}", Diff(cfg, tmpl))
 	mux.HandleFunc("GET /{repo}/refs/", Refs(cfg, tmpl))
 	mux.HandleFunc("GET /{repo}/settings", SettingsGet(cfg, tmpl))
 	mux.HandleFunc("POST /{repo}/settings", SettingsPost(cfg, tmpl))
diff --git a/repo_handlers.go b/repo_handlers.go
index bcd16e6..10b569f 100644
--- a/repo_handlers.go
+++ b/repo_handlers.go
@@ -138,6 +138,30 @@ func Repos(cfg Config, tmpl *template.Template) http.HandlerFunc {
 	}
 }
 
+// splitRefPath splits a combined "ref/path" URL segment into a git ref and
+// a filesystem path by matching increasingly long prefixes against known
+// branches and tags. Falls back to treating the first segment as the ref.
+func splitRefPath(git *Git, refpath string) (ref, path string) {
+	if refpath == "" {
+		return git.DefaultBranch(), ""
+	}
+	refs := make(map[string]bool)
+	for _, b := range git.Branches() {
+		refs[b] = true
+	}
+	for _, t := range git.Tags() {
+		refs[t] = true
+	}
+	parts := strings.Split(refpath, "/")
+	for i := len(parts); i > 0; i-- {
+		candidate := strings.Join(parts[:i], "/")
+		if refs[candidate] {
+			return candidate, strings.Join(parts[i:], "/")
+		}
+	}
+	return parts[0], strings.Join(parts[1:], "/")
+}
+
 func Tree(cfg Config, tmpl *template.Template) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		type FileEntry struct {
@@ -148,10 +172,10 @@ func Tree(cfg Config, tmpl *template.Template) http.HandlerFunc {
 		if !checkRepoAccess(w, r, git) {
 			return
 		}
-		treePath := r.PathValue("path")
-		ref := r.PathValue("ref")
-		if ref == "" {
-			ref = git.DefaultBranch()
+		treePath := ""
+		ref := git.DefaultBranch()
+		if rp := r.PathValue("refpath"); rp != "" {
+			ref, treePath = splitRefPath(git, rp)
 		}
 		cloneSSH := ""
 		cloneHTTPS := ""
@@ -229,11 +253,7 @@ func Blob(cfg Config, tmpl *template.Template) http.HandlerFunc {
 		if !checkRepoAccess(w, r, git) {
 			return
 		}
-		filePath := r.PathValue("path")
-		ref := r.PathValue("ref")
-		if ref == "" {
-			ref = git.DefaultBranch()
-		}
+		ref, filePath := splitRefPath(git, r.PathValue("refpath"))
 
 		content, err := git.Blob(ref, filePath)
 		if err != nil {
@@ -262,7 +282,7 @@ func Log(cfg Config, tmpl *template.Template) http.HandlerFunc {
 		if !checkRepoAccess(w, r, git) {
 			return
 		}
-		ref := r.PathValue("ref")
+		ref, _ := splitRefPath(git, r.PathValue("refpath"))
 		commits, err := git.Log(ref, 50)
 		if err != nil {
 			renderError(w, tmpl, http.StatusInternalServerError, err.Error())
@@ -290,7 +310,7 @@ func Diff(cfg Config, tmpl *template.Template) http.HandlerFunc {
 		if !checkRepoAccess(w, r, git) {
 			return
 		}
-		ref := r.PathValue("ref")
+		ref := r.PathValue("hash")
 		commit, err := git.Diff(ref)
 		if err != nil || len(commit) == 0 {
 			renderError(w, tmpl, http.StatusNotFound, "commit not found")