package web import ( "html/template" "net/http" "regexp" "strings" "git.josie-c.com/josie/simplegit/internal/git" "git.josie-c.com/josie/simplegit/internal/render" ) const commitsPerPage = 50 var shaPattern = regexp.MustCompile(`^[0-9a-fA-F]{4,40}$`) type commitRow struct { SHA string // short, 7 chars Href string Subject string Author string Date string // trimmed ISO timestamp } type commitsData struct { navData Ref string Commits []commitRow } type commitData struct { navData SHA string Subject string Body string Author string Email string Date string Parents []commitRow TreeHref string DiffHTML template.HTML Notice string } func (s *Server) handleCommits(w http.ResponseWriter, r *http.Request) { repo, repoPath, user, ok := s.resolveRepo(w, r) if !ok { return } owner := r.PathValue("user") ref := strings.Trim(r.PathValue("ref"), "/") if ref == "" { ref = repo.DefaultBranch } if !validRef(ref) { http.NotFound(w, r) return } exists, err := git.RefExists(repoPath, ref) if err != nil { s.internalError(w, r, err) return } if !exists { http.NotFound(w, r) return } base := "/" + owner + "/" + repo.Name log, err := git.Log(repoPath, ref, commitsPerPage) if err != nil { s.internalError(w, r, err) return } data := commitsData{navData: nav(r, repo, user, "code"), Ref: ref} for _, c := range log { data.Commits = append(data.Commits, commitRow{ SHA: shortSHA(c.SHA), Href: base + "/commit/" + c.SHA, Subject: c.Subject, Author: c.Author, Date: trimDate(c.Date), }) } s.render(w, "commits.html", http.StatusOK, data) } func (s *Server) handleCommit(w http.ResponseWriter, r *http.Request) { repo, repoPath, user, ok := s.resolveRepo(w, r) if !ok { return } owner := r.PathValue("user") sha := r.PathValue("sha") if !shaPattern.MatchString(sha) { http.NotFound(w, r) return } base := "/" + owner + "/" + repo.Name c, err := git.CommitAt(repoPath, sha) if err != nil { http.NotFound(w, r) return } data := commitData{ navData: nav(r, repo, user, "code"), SHA: c.SHA, Subject: c.Subject, Body: c.Body, Author: c.Author, Email: c.Email, Date: trimDate(c.Date), TreeHref: base + "/tree/" + c.SHA + "/", } for _, p := range c.Parents { if pc, err := git.CommitAt(repoPath, p); err == nil { data.Parents = append(data.Parents, commitRow{ SHA: shortSHA(p), Href: base + "/commit/" + p, Subject: pc.Subject, }) } else { data.Parents = append(data.Parents, commitRow{SHA: shortSHA(p), Href: base + "/commit/" + p}) } } patch, err := git.ShowPatch(repoPath, c.SHA, maxDiffBytes) if err != nil { s.internalError(w, r, err) return } if len(patch) > 0 { data.DiffHTML, data.Notice = renderDiffHTML(patch) } s.render(w, "commit.html", http.StatusOK, data) } // renderDiffHTML caps a patch at maxDiffBytes and renders it highlighted, // falling back to an escaped
; the notice is set when truncated.
// Shared by the commit view and the PR diff.
func renderDiffHTML(patch []byte) (template.HTML, string) {
	notice := ""
	if len(patch) > maxDiffBytes {
		patch = patch[:maxDiffBytes]
		notice = diffTruncatedNotice
	}
	if html, handled, err := render.CodeHTML(diffLexeme, string(patch)); err == nil && handled {
		return template.HTML(html), notice
	}
	return template.HTML("
" + template.HTMLEscapeString(string(patch)) + "
"), notice } func shortSHA(sha string) string { return sha[:min(len(sha), 7)] } func trimDate(iso string) string { if len(iso) >= 16 { return iso[:16] } return iso }