Aucune description

quit.go 4.3KB

    package main import ( "bytes" "html/template" "io" "log" "net/http" "strings" "time" "github.com/libgit2/git2go" ) func main() { repo, err := git.OpenRepository("/home/lu/t/libgit2") if err != nil { log.Fatal("opening repository: ", err) } http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { buf := new(bytes.Buffer) err = repoTmpl.Execute(buf, map[string]interface{}{ "RepoPath": "~/t/libgit2", "Repo": NewFancyRepo(repo), "Style": template.CSS(repoStyle), }) if err != nil { log.Println("rendering repo: ", err) http.Error(w, "internal server error", http.StatusInternalServerError) return } io.Copy(w, buf) }) log.Fatal(http.ListenAndServe("localhost:12345", nil)) } type FancyRepo struct { repo *git.Repository commit *FancyCommit } func NewFancyRepo(repo *git.Repository) *FancyRepo { return &FancyRepo{repo: repo} } func (fr *FancyRepo) Commit() (*FancyCommit, error) { if fr.commit == nil { head, err := fr.repo.Head() if err != nil { return nil, err } commit, err := fr.repo.LookupCommit(head.Target()) if err != nil { return nil, err } fr.commit = &FancyCommit{commit: commit} } return fr.commit, nil } type FancyCommit struct { commit *git.Commit files []*FancyFile } func (fc *FancyCommit) Author() string { return fc.commit.Author().Name } func (fc *FancyCommit) Date() time.Time { return fc.commit.Author().When } func (fc *FancyCommit) Id(n int) string { if n == 0 { return fc.commit.TreeId().String() } return fc.commit.TreeId().String()[:n] } func (fc *FancyCommit) Summary() string { return fc.commit.Summary() } func (fc *FancyCommit) Description() string { return fc.commit.Message() } func (fc *FancyCommit) Files() ([]*FancyFile, error) { if fc.files == nil { tree, err := fc.commit.Tree() if err != nil { return nil, err } err = tree.Walk(func(s string, entry *git.TreeEntry) int { fc.files = append(fc.files, &FancyFile{ repo: fc.commit.Owner(), entry: entry, }) return 1 }) if err != nil { return nil, err } } return fc.files, nil } type FancyFile struct { repo *git.Repository entry *git.TreeEntry commit *FancyCommit } func (fc *FancyFile) Name() string { return fc.entry.Name } func (fc *FancyFile) Commit() (*FancyCommit, error) { if fc.commit == nil { commit, err := fc.repo.LookupCommit(fc.entry.Id) if err != nil { return nil, err } fc.commit = &FancyCommit{commit: commit} } return fc.commit, nil } func (fc *FancyFile) Type() string { return strings.ToLower(fc.entry.Type.String()) } var repoTmpl = template.Must(template.New("").Parse(`<!doctype html> <html> <head> <meta charset="utf-8" /> <title>{{ .RepoPath }}</title> <style>{{ .Style }}</style> </head> <body> <section id="commit" class="commit-info"> <div class="commit-author">{{ .Repo.Commit.Author }}</div> <div class="commit-summary" title="{{ .Repo.Commit.Description }}">{{ .Repo.Commit.Summary }}</div> <div class="commit-id" data-commit="{{ .Repo.Commit.Id 0 }}"><a href="#">{{ .Repo.Commit.Id 10 }}</a></div> <time class="commit-date">{{ .Repo.Commit.Date }}</time> </section> <table id="files"> {{ range $file := .Repo.Commit.Files }} <tr class="file"> <td class="file-name file-type-{{ $file.Type }}">{{ $file.Name }}</td> </tr> {{ end }} </body> </html> `)) var repoStyle = ` * { box-sizing: border-box; } body { font-family: Arial, sans-serif; } #commit { display: flex; justify-content: space-between; align-items: baseline; padding: 0.6em 0.5em; width: 70vw; background-color: rgba(200, 0, 255, 0.1); border: 1px solid rgba(200, 0, 255, 0.15); border-radius: 3px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } #commit .commit-author { font-weight: 600; } #commit .commit-id { font-family: monospace; } #files td { border: 1px solid rgba(200, 0, 200, 0.15); border-radius: 3px; border-bottom-left-radius: 0; border-bottom-right-radius: 0; padding: 0.2em 0.5em; font-size: 95%; } #files tr:hover { background-color: #efefef; } #files { width: 70vw; } .file-name { color: #00445a; } .file-type-blob:before { content:"🖹"; padding-right: 0.5em; } .file-type-tree:before { content: "🗀"; padding-right: 0.2em; } .file-type-tree:after { content: "/"; } `