From 60fcbdd30dd421a604bd0cbdf70c4ed5363622d8 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Wed, 19 Aug 2020 23:54:23 +0500 Subject: [PATCH] WIP history page --- data | 1 + go.mod | 6 +-- go.sum | 28 ++----------- history/history.go | 4 +- history/sh.go | 100 +++++++++++++++++++++++++++++++++++++++++++++ http_mutators.go | 12 +++--- http_readers.go | 44 ++++++++++++++++++++ main.go | 5 ++- metarrhiza | 2 +- tree/tree.go | 9 ++-- 10 files changed, 169 insertions(+), 42 deletions(-) create mode 160000 data create mode 100644 history/sh.go diff --git a/data b/data new file mode 160000 index 0000000..b2e7803 --- /dev/null +++ b/data @@ -0,0 +1 @@ +Subproject commit b2e78031973e4a6844d943333e823c0714e7a673 diff --git a/go.mod b/go.mod index 52eea2b..a66370d 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,4 @@ module github.com/bouncepaw/mycorrhiza go 1.14 -require ( - github.com/go-git/go-git/v5 v5.1.0 - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 // indirect -) +require github.com/go-git/go-git/v5 v5.1.0 diff --git a/go.sum b/go.sum index 594e00c..66994b3 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git v1.0.0 h1:YcN9iDGDoXuIw0vHls6rINwV416HYa0EB2X+RBsyYp4= -github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk= github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= @@ -40,41 +38,21 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6 h1:qKpj8TpV+LEhel7H/fR788J+KvhWZ3o3V6N2fU/iuLU= -golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/history/history.go b/history/history.go index 3d9d081..5a54ac0 100644 --- a/history/history.go +++ b/history/history.go @@ -3,6 +3,7 @@ package history import ( "fmt" "log" + "os" "time" "github.com/bouncepaw/mycorrhiza/util" @@ -107,9 +108,10 @@ func (hop *HistoryOp) Apply() *HistoryOp { return hop } +// Rename renames from `from` to `to`. NB. It uses os.Rename internally rather than git.Move because git.Move works wrong for some reason. func Rename(from, to string) error { log.Println(util.ShorterPath(from), util.ShorterPath(to)) - _, err := Worktree.Move(util.ShorterPath(from), util.ShorterPath(to)) + err := os.Rename(from, to) return err } diff --git a/history/sh.go b/history/sh.go new file mode 100644 index 0000000..34af389 --- /dev/null +++ b/history/sh.go @@ -0,0 +1,100 @@ +package history + +import ( + "bytes" + "fmt" + "log" + "os/exec" + "regexp" + "strconv" + "strings" + "time" +) + +type Revision struct { + Hash string + Username string + Time time.Time + Message string +} + +var gitpath string + +func init() { + path, err := exec.LookPath("git") + if err != nil { + log.Fatal("Cound not find the git executable. Check your $PATH.") + } else { + log.Println("Git path is", path) + } + gitpath = path +} + +// I pronounce it as [gɪt͡ʃ]. +func gitsh(args ...string) (out bytes.Buffer, err error) { + cmd := exec.Command(gitpath, args...) + cmd.Stdout = &out + cmd.Run() + if err != nil { + log.Println("gitsh:", err) + } + return out, err +} + +func Revisions(filepath string) ([]Revision, error) { + if filepath == "" { + return []Revision{}, nil + } + var ( + out, err = gitsh( + "log", "--oneline", "--no-merges", + // Hash, Commiter email, Commiter time, Commit msg separated by tab + "--pretty=format:\"%h\t%ce\t%ct\t%s\"", + "--", filepath, + ) + revs []Revision + ) + if err == nil { + for _, line := range strings.Split(out.String(), "\n") { + if rev := parseRevisionLine(line); rev != nil { + revs = append(revs, *rev) + } + } + } + return revs, err +} + +func unixTimestampAsTime(ts string) *time.Time { + i, err := strconv.ParseInt(ts, 10, 64) + if err != nil { + return nil + } + tm := time.Unix(i, 0) + return &tm +} + +// This regex is wrapped in "". For some reason, these quotes appear at some time and we have to get rid of them. +var revisionLinePattern = regexp.MustCompile("\"(.*)\t(.*)@.*\t(.*)\t(.*)\"") + +func parseRevisionLine(line string) *Revision { + var ( + results = revisionLinePattern.FindStringSubmatch(line) + rev = Revision{ + Hash: results[1], + Username: results[2], + Time: *unixTimestampAsTime(results[3]), + Message: results[4], + } + ) + return &rev +} + +func (rev *Revision) AsHtmlTableRow() string { + return fmt.Sprintf(` + + %s + %s + + %s +`, rev.Hash, rev.Username, rev.Time.String(), rev.Message) +} diff --git a/http_mutators.go b/http_mutators.go index c943da3..7012839 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -134,11 +134,6 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil { log.Println(err) } - if err = ioutil.WriteFile(fullPath, data, 0644); err != nil { - HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", - "Could not save passed data") - return - } if !isOld { HyphaStorage[hyphaName] = &HyphaData{ binaryPath: fullPath, @@ -155,9 +150,14 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) { hyphaData.binaryPath = fullPath hyphaData.binaryType = mimeType } + if err = ioutil.WriteFile(fullPath, data, 0644); err != nil { + HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", + "Could not save passed data") + return + } log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath) history.Operation(history.TypeEditText). - WithFiles(fullPath). + WithFiles(fullPath, hyphaData.binaryPath). WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mimeType.Mime())). WithSignature("anon"). Apply() diff --git a/http_readers.go b/http_readers.go index fab83ee..889d83d 100644 --- a/http_readers.go +++ b/http_readers.go @@ -8,6 +8,7 @@ import ( "os" "github.com/bouncepaw/mycorrhiza/gemtext" + "github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/tree" ) @@ -15,6 +16,49 @@ func init() { http.HandleFunc("/page/", handlerPage) http.HandleFunc("/text/", handlerText) http.HandleFunc("/binary/", handlerBinary) + http.HandleFunc("/history/", handlerHistory) +} + +// handlerHistory lists all revisions of a hypha +func handlerHistory(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + hyphaName := HyphaNameFromRq(rq, "history") + var tbody string + if data, ok := HyphaStorage[hyphaName]; ok { + revsT, err := history.Revisions(data.textPath) + if err == nil { + for _, rev := range revsT { + tbody += rev.AsHtmlTableRow() + } + } + revsB, err := history.Revisions(data.binaryPath) + if err == nil { + for _, rev := range revsB { + tbody += rev.AsHtmlTableRow() + } + } + log.Println(revsT, revsB) + } + + table := fmt.Sprintf(` +
+ + + + + + + + + + + %s + +
HashUsernameTimeMessage
+
`, tbody) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(base(hyphaName, table))) } // handlerText serves raw source text of the hypha. diff --git a/main.go b/main.go index cab791b..39663cc 100644 --- a/main.go +++ b/main.go @@ -130,6 +130,9 @@ func main() { if err != nil { log.Fatal(err) } + if err := os.Chdir(WikiDir); err != nil { + log.Fatal(err) + } log.Println("Wiki storage directory is", WikiDir) log.Println("Start indexing hyphae...") Index(WikiDir) @@ -138,7 +141,7 @@ func main() { history.Start(WikiDir) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static")))) - // See http_readers.go for /page/, /text/, /binary/. + // See http_readers.go for /page/, /text/, /binary/, /history/. // See http_mutators.go for /upload-binary/, /upload-text/, /edit/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) diff --git a/metarrhiza b/metarrhiza index 30287be..2f58d4e 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 30287be4496638e342878e2adf50e0a74ce71a5f +Subproject commit 2f58d4e9663806496d7edfdeb24098abe986f454 diff --git a/tree/tree.go b/tree/tree.go index d7e8c45..1655664 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -60,9 +60,6 @@ func (t *tree) fill() { // It applies itself recursively on the tree's children. func (t *tree) asHtml() (html string) { if t.root { - for _, siblingName := range t.siblings { - html += navitreeEntry(siblingName, "navitree__sibling") - } html += navitreeEntry(t.name, "navitree__pagename") } else { html += navitreeEntry(t.name, "navitree__name") @@ -72,6 +69,12 @@ func (t *tree) asHtml() (html string) { html += subtree.asHtml() } + if t.root { + for _, siblingName := range t.siblings { + html += navitreeEntry(siblingName, "navitree__sibling") + } + } + return `` }