git, routes: commit diff view
This commit is contained in:
parent
ce71721c6d
commit
c165c44768
6 changed files with 198 additions and 0 deletions
99
git/diff.go
Normal file
99
git/diff.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bluekeyes/go-gitdiff/gitdiff"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TextFragment struct {
|
||||||
|
Header string
|
||||||
|
Lines []gitdiff.Line
|
||||||
|
}
|
||||||
|
|
||||||
|
type Diff struct {
|
||||||
|
Name struct {
|
||||||
|
Old string
|
||||||
|
New string
|
||||||
|
}
|
||||||
|
TextFragments []TextFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
// A nicer git diff representation.
|
||||||
|
type NiceDiff struct {
|
||||||
|
Commit struct {
|
||||||
|
Message string
|
||||||
|
Author object.Signature
|
||||||
|
This string
|
||||||
|
Parent string
|
||||||
|
}
|
||||||
|
Stat struct {
|
||||||
|
FilesChanged int
|
||||||
|
Insertions int
|
||||||
|
Deletions int
|
||||||
|
}
|
||||||
|
Diff []Diff
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GitRepo) Diff() (*NiceDiff, error) {
|
||||||
|
c, err := g.r.CommitObject(g.h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("commit object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent *object.Commit
|
||||||
|
if len(c.ParentHashes) > 0 {
|
||||||
|
parent, err = c.Parent(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting parent: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent = c
|
||||||
|
}
|
||||||
|
|
||||||
|
patch, err := parent.Patch(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("patch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String()))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nd := NiceDiff{}
|
||||||
|
nd.Commit.This = c.Hash.String()
|
||||||
|
nd.Commit.Parent = parent.Hash.String()
|
||||||
|
nd.Commit.Author = c.Author
|
||||||
|
nd.Commit.Message = c.Message
|
||||||
|
ndiff := Diff{}
|
||||||
|
|
||||||
|
for _, d := range diffs {
|
||||||
|
ndiff.Name.New = d.NewName
|
||||||
|
ndiff.Name.Old = d.OldName
|
||||||
|
|
||||||
|
for _, tf := range d.TextFragments {
|
||||||
|
ndiff.TextFragments = append(ndiff.TextFragments, TextFragment{
|
||||||
|
Header: tf.Header(),
|
||||||
|
Lines: tf.Lines,
|
||||||
|
})
|
||||||
|
for _, l := range tf.Lines {
|
||||||
|
switch l.Op {
|
||||||
|
case gitdiff.OpAdd:
|
||||||
|
nd.Stat.Insertions += 1
|
||||||
|
case gitdiff.OpDelete:
|
||||||
|
nd.Stat.Deletions += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nd.Diff = append(nd.Diff, ndiff)
|
||||||
|
}
|
||||||
|
|
||||||
|
nd.Stat.FilesChanged = len(diffs)
|
||||||
|
|
||||||
|
return &nd, nil
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -12,6 +12,7 @@ require (
|
||||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
|
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
|
||||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||||
|
github.com/bluekeyes/go-gitdiff v0.7.0 // indirect
|
||||||
github.com/cloudflare/circl v1.1.0 // indirect
|
github.com/cloudflare/circl v1.1.0 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.0 // indirect
|
github.com/go-git/gcfg v1.5.0 // indirect
|
||||||
|
@ -22,6 +23,7 @@ require (
|
||||||
github.com/pjbgf/sha1cd v0.2.0 // indirect
|
github.com/pjbgf/sha1cd v0.2.0 // indirect
|
||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/skeema/knownhosts v1.1.0 // indirect
|
github.com/skeema/knownhosts v1.1.0 // indirect
|
||||||
|
github.com/sourcegraph/go-diff v0.6.1 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
||||||
golang.org/x/crypto v0.3.0 // indirect
|
golang.org/x/crypto v0.3.0 // indirect
|
||||||
golang.org/x/net v0.2.0 // indirect
|
golang.org/x/net v0.2.0 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -10,6 +10,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
github.com/bluekeyes/go-gitdiff v0.7.0 h1:w4SrRFcufU0/tEpWx3VurDBAnWfpxsmwS7yWr14meQk=
|
||||||
|
github.com/bluekeyes/go-gitdiff v0.7.0/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
|
||||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
|
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
|
||||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||||
|
@ -29,6 +31,7 @@ github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlK
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||||
github.com/go-git/go-git/v5 v5.5.0 h1:StO/ASRvk1Pp74tr7XQ0pQwKlCFignzzTF/NLKdQzUE=
|
github.com/go-git/go-git/v5 v5.5.0 h1:StO/ASRvk1Pp74tr7XQ0pQwKlCFignzzTF/NLKdQzUE=
|
||||||
github.com/go-git/go-git/v5 v5.5.0/go.mod h1:g456XI30HAdt7GQtIf8JR6GDAdULGaR4KtfFtQa0uTg=
|
github.com/go-git/go-git/v5 v5.5.0/go.mod h1:g456XI30HAdt7GQtIf8JR6GDAdULGaR4KtfFtQa0uTg=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
|
@ -56,9 +59,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
|
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
|
||||||
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
|
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
|
||||||
|
github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ=
|
||||||
|
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
@ -115,6 +122,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -12,5 +12,6 @@ func Handlers(c *config.Config) *flow.Mux {
|
||||||
mux.HandleFunc("/:name/tree/:ref/...", d.RepoTree, "GET")
|
mux.HandleFunc("/:name/tree/:ref/...", d.RepoTree, "GET")
|
||||||
mux.HandleFunc("/:name/blob/:ref/...", d.FileContent, "GET")
|
mux.HandleFunc("/:name/blob/:ref/...", d.FileContent, "GET")
|
||||||
mux.HandleFunc("/:name/log/:ref", d.Log, "GET")
|
mux.HandleFunc("/:name/log/:ref", d.Log, "GET")
|
||||||
|
mux.HandleFunc("/:name/commit/:ref", d.Diff, "GET")
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,3 +127,39 @@ func (d *deps) Log(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *deps) Diff(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := flow.Param(r.Context(), "name")
|
||||||
|
ref := flow.Param(r.Context(), "ref")
|
||||||
|
|
||||||
|
path := filepath.Join(d.c.Git.ScanPath, name+".git")
|
||||||
|
gr, err := git.Open(path, ref)
|
||||||
|
if err != nil {
|
||||||
|
Write404(w, *d.c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, err := gr.Diff()
|
||||||
|
if err != nil {
|
||||||
|
Write500(w, *d.c)
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tpath := filepath.Join(d.c.Template.Dir, "*")
|
||||||
|
t := template.Must(template.ParseGlob(tpath))
|
||||||
|
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
|
||||||
|
data["commit"] = diff.Commit
|
||||||
|
data["stat"] = diff.Stat
|
||||||
|
data["diff"] = diff.Diff
|
||||||
|
data["meta"] = d.c.Meta
|
||||||
|
data["name"] = name
|
||||||
|
data["ref"] = ref
|
||||||
|
|
||||||
|
if err := t.ExecuteTemplate(w, "commit", data); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
52
templates/commit.html
Normal file
52
templates/commit.html
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{{ define "commit" }}
|
||||||
|
<html>
|
||||||
|
{{ template "head" . }}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>{{ .meta.Title }}</h1>
|
||||||
|
<h2>{{ .meta.Description }}</h2>
|
||||||
|
</header>
|
||||||
|
<body>
|
||||||
|
{{ template "nav" . }}
|
||||||
|
<main>
|
||||||
|
<section>
|
||||||
|
<p>author: {{ .commit.Author.Name }} <{{ .commit.Author.Email}}> on {{ .commit.Author.When }}</p>
|
||||||
|
<p>commit: <a href="/{{ .name }}/commit/{{ .commit.This }}">
|
||||||
|
{{ .commit.This }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>parent: <a href="/{{ .name }}/commit/{{ .commit.Parent }}">
|
||||||
|
{{ .commit.Parent }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>{{ .stat.FilesChanged }} files changed,
|
||||||
|
{{ .stat.Insertions }} insertions(+),
|
||||||
|
{{ .stat.Deletions }} deletions(-)
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
{{ range .diff }}
|
||||||
|
{{ if .Name.Old }}
|
||||||
|
<p>{{ .Name.Old }} → {{ .Name.New }}</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>{{.Name.New }}</p>
|
||||||
|
{{- end -}}
|
||||||
|
<pre>
|
||||||
|
{{- range .TextFragments -}}
|
||||||
|
<p>{{- .Header -}}</p>
|
||||||
|
{{- range .Lines -}}
|
||||||
|
{{- if eq .Op.String "+" -}}
|
||||||
|
<span style="color: green">{{ .String }}</span>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if eq .Op.String "-" -}}
|
||||||
|
<span style="color: red">{{ .String }}</span>
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
</pre>
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
Loading…
Reference in a new issue