Print some diffs in web feeds

This commit is contained in:
bouncepaw 2021-05-03 23:31:19 +05:00
parent 7c1f38d5b0
commit 4ff51352af
3 changed files with 72 additions and 16 deletions

View File

@ -3,6 +3,7 @@ package history
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"html"
"log" "log"
"os/exec" "os/exec"
"regexp" "regexp"
@ -44,9 +45,26 @@ type Revision struct {
Username string Username string
Time time.Time Time time.Time
Message string Message string
filesAffectedBuf []string
hyphaeAffectedBuf []string hyphaeAffectedBuf []string
} }
// filesAffected tells what files have been affected by the revision.
func (rev *Revision) filesAffected() (filenames []string) {
if nil != rev.filesAffectedBuf {
return rev.filesAffectedBuf
}
// List of files affected by this revision, one per line.
out, err := silentGitsh("diff-tree", "--no-commit-id", "--name-only", "-r", rev.Hash)
// There's an error? Well, whatever, let's just assign an empty slice, who cares.
if err != nil {
rev.filesAffectedBuf = []string{}
} else {
rev.filesAffectedBuf = strings.Split(out.String(), "\n")
}
return rev.filesAffectedBuf
}
// determine what hyphae were affected by this revision // determine what hyphae were affected by this revision
func (rev *Revision) hyphaeAffected() (hyphae []string) { func (rev *Revision) hyphaeAffected() (hyphae []string) {
if nil != rev.hyphaeAffectedBuf { if nil != rev.hyphaeAffectedBuf {
@ -54,8 +72,6 @@ func (rev *Revision) hyphaeAffected() (hyphae []string) {
} }
hyphae = make([]string, 0) hyphae = make([]string, 0)
var ( var (
// List of files affected by this revision, one per line.
out, err = gitsh("diff-tree", "--no-commit-id", "--name-only", "-r", rev.Hash)
// set is used to determine if a certain hypha has been already noted (hyphae are stored in 2 files at most currently). // set is used to determine if a certain hypha has been already noted (hyphae are stored in 2 files at most currently).
set = make(map[string]bool) set = make(map[string]bool)
isNewName = func(hyphaName string) bool { isNewName = func(hyphaName string) bool {
@ -65,11 +81,9 @@ func (rev *Revision) hyphaeAffected() (hyphae []string) {
set[hyphaName] = true set[hyphaName] = true
return true return true
} }
filesAffected = rev.filesAffected()
) )
if err != nil { for _, filename := range filesAffected {
return hyphae
}
for _, filename := range strings.Split(out.String(), "\n") {
if strings.IndexRune(filename, '.') >= 0 { if strings.IndexRune(filename, '.') >= 0 {
dotPos := strings.LastIndexByte(filename, '.') dotPos := strings.LastIndexByte(filename, '.')
hyphaName := string([]byte(filename)[0:dotPos]) // is it safe? hyphaName := string([]byte(filename)[0:dotPos]) // is it safe?
@ -94,15 +108,44 @@ func (rev Revision) HyphaeLinksHTML() (html string) {
if i > 0 { if i > 0 {
html += `<span aria-hidden="true">, </span>` html += `<span aria-hidden="true">, </span>`
} }
html += fmt.Sprintf(`<a href="/page/%[1]s">%[1]s</a>`, hyphaName) html += fmt.Sprintf(`<a href="/hypha/%[1]s">%[1]s</a>`, hyphaName)
} }
return html return html
} }
func (rev *Revision) descriptionForFeed() (html string) { // descriptionForFeed generates a good enough HTML contents for a web feed.
func (rev *Revision) descriptionForFeed() (htmlDesc string) {
return fmt.Sprintf( return fmt.Sprintf(
`<p>%s</p> `<p>%s</p>
<p><b>Hyphae affected:</b> %s</p>`, rev.Message, rev.HyphaeLinksHTML()) <p><b>Hyphae affected:</b> %s</p>
<pre><code>%s</code></pre>`, rev.Message, rev.HyphaeLinksHTML(), html.EscapeString(rev.textDiff()))
}
// textDiff generates a good enough diff to display in a web feed. It is not html-escaped.
func (rev *Revision) textDiff() (diff string) {
filenames, ok := rev.mycoFiles()
if !ok {
return "No text changes"
}
for _, filename := range filenames {
text, err := PrimitiveDiffAtRevision(filename, rev.Hash)
if err != nil {
diff += "\nAn error has occured with " + filename + "\n"
}
diff += text + "\n"
}
return diff
}
// mycoFiles returns filenames of .myco file. It is not ok if there are no myco files.
func (rev *Revision) mycoFiles() (filenames []string, ok bool) {
filenames = []string{}
for _, filename := range rev.filesAffected() {
if strings.HasSuffix(filename, ".myco") {
filenames = append(filenames, filename)
}
}
return filenames, len(filenames) > 0
} }
// Try and guess what link is the most important by looking at the message. // Try and guess what link is the most important by looking at the message.
@ -113,11 +156,11 @@ func (rev *Revision) bestLink() string {
) )
switch { switch {
case renameRes != nil: case renameRes != nil:
return "/page/" + renameRes[1] return "/hypha/" + renameRes[1]
case len(revs) == 0: case len(revs) == 0:
return "" return ""
default: default:
return "/page/" + revs[0] return "/hypha/" + revs[0]
} }
} }
@ -135,6 +178,15 @@ func gitsh(args ...string) (out bytes.Buffer, err error) {
return *bytes.NewBuffer(b), err return *bytes.NewBuffer(b), err
} }
// silentGitsh is like gitsh, except it writes less to the stdout.
func silentGitsh(args ...string) (out bytes.Buffer, err error) {
cmd := exec.Command(gitpath, args...)
cmd.Dir = util.WikiDir
b, err := cmd.CombinedOutput()
return *bytes.NewBuffer(b), err
}
// Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted. // Convert a UNIX timestamp as string into a time. If nil is returned, it means that the timestamp could not be converted.
func unixTimestampAsTime(ts string) *time.Time { func unixTimestampAsTime(ts string) *time.Time {
i, err := strconv.ParseInt(ts, 10, 64) i, err := strconv.ParseInt(ts, 10, 64)

View File

@ -4,6 +4,7 @@ package history
import ( import (
"fmt" "fmt"
"log"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -22,7 +23,7 @@ func recentChangesFeed() *feeds.Feed {
Updated: time.Now(), Updated: time.Now(),
} }
var ( var (
out, err = gitsh( out, err = silentGitsh(
"log", "--oneline", "--no-merges", "log", "--oneline", "--no-merges",
"--pretty=format:\"%h\t%ae\t%at\t%s\"", "--pretty=format:\"%h\t%ae\t%at\t%s\"",
"--max-count=30", "--max-count=30",
@ -34,6 +35,7 @@ func recentChangesFeed() *feeds.Feed {
revs = append(revs, parseRevisionLine(line)) revs = append(revs, parseRevisionLine(line))
} }
} }
log.Printf("Found %d recent changes", len(revs))
for _, rev := range revs { for _, rev := range revs {
feed.Add(&feeds.Item{ feed.Add(&feeds.Item{
Title: rev.Message, Title: rev.Message,
@ -62,7 +64,7 @@ func RecentChangesJSON() (string, error) {
func RecentChanges(n int) []Revision { func RecentChanges(n int) []Revision {
var ( var (
out, err = gitsh( out, err = silentGitsh(
"log", "--oneline", "--no-merges", "log", "--oneline", "--no-merges",
"--pretty=format:\"%h\t%ae\t%at\t%s\"", "--pretty=format:\"%h\t%ae\t%at\t%s\"",
"--max-count="+strconv.Itoa(n), "--max-count="+strconv.Itoa(n),
@ -74,6 +76,7 @@ func RecentChanges(n int) []Revision {
revs = append(revs, parseRevisionLine(line)) revs = append(revs, parseRevisionLine(line))
} }
} }
log.Printf("Found %d recent changes", len(revs))
return revs return revs
} }
@ -86,7 +89,7 @@ func FileChanged(path string) bool {
// Revisions returns slice of revisions for the given hypha name. // Revisions returns slice of revisions for the given hypha name.
func Revisions(hyphaName string) ([]Revision, error) { func Revisions(hyphaName string) ([]Revision, error) {
var ( var (
out, err = gitsh( out, err = silentGitsh(
"log", "--oneline", "--no-merges", "log", "--oneline", "--no-merges",
// Hash, author email, author time, commit msg separated by tab // Hash, author email, author time, commit msg separated by tab
"--pretty=format:\"%h\t%ae\t%at\t%s\"", "--pretty=format:\"%h\t%ae\t%at\t%s\"",
@ -101,6 +104,7 @@ func Revisions(hyphaName string) ([]Revision, error) {
} }
} }
} }
log.Printf("Found %d revisions for %s\n", len(revs), hyphaName)
return revs, err return revs, err
} }
@ -177,6 +181,6 @@ func FileAtRevision(filepath, hash string) (string, error) {
} }
func PrimitiveDiffAtRevision(filepath, hash string) (string, error) { func PrimitiveDiffAtRevision(filepath, hash string) (string, error) {
out, err := gitsh("diff", "--unified=1", "--no-color", hash+"~", hash, "--", filepath) out, err := silentGitsh("diff", "--unified=1", "--no-color", hash+"~", hash, "--", filepath)
return out.String(), err return out.String(), err
} }

View File

@ -352,7 +352,7 @@ func StreamAboutHTML(qw422016 *qt422016.Writer) {
//line views/stuff.qtpl:98 //line views/stuff.qtpl:98
qw422016.N().S(`</h1> qw422016.N().S(`</h1>
<ul> <ul>
<li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.1.0</li> <li><b><a href="https://mycorrhiza.lesarbr.es">MycorrhizaWiki</a> version:</b> 1.2.0 indev</li>
`) `)
//line views/stuff.qtpl:101 //line views/stuff.qtpl:101
if user.AuthUsed { if user.AuthUsed {