mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-11 21:10:26 +00:00
Make recent-changes feeds able to look farther back for revisions.
Before, grouped feeds would only use the last 30 revisions. If part of a group was outside these revisions, it would be handled incorrectly. Now, grouped feeds contain the last 30 groups, and use as many revisions as needed for this.
This commit is contained in:
parent
6cb4161dd8
commit
8ed6dd15bf
@ -12,14 +12,16 @@ import (
|
||||
"github.com/gorilla/feeds"
|
||||
)
|
||||
|
||||
const changeGroupMaxSize = 100
|
||||
|
||||
func recentChangesFeed(opts FeedOptions) *feeds.Feed {
|
||||
feed := &feeds.Feed{
|
||||
Title: cfg.WikiName + " (recent changes)",
|
||||
Link: &feeds.Link{Href: cfg.URL},
|
||||
Description: "List of 30 recent changes on the wiki",
|
||||
Description: fmt.Sprintf("List of %d recent changes on the wiki", changeGroupMaxSize),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
revs := RecentChanges(30)
|
||||
revs := newRecentChangesStream()
|
||||
groups := opts.grouping.Group(revs)
|
||||
for _, grp := range groups {
|
||||
item := grp.feedItem(opts)
|
||||
@ -43,6 +45,7 @@ func RecentChangesJSON(opts FeedOptions) (string, error) {
|
||||
return recentChangesFeed(opts).ToJSON()
|
||||
}
|
||||
|
||||
// revisionGroup is a slice of revisions, ordered most recent first.
|
||||
type revisionGroup []Revision
|
||||
|
||||
func newRevisionGroup(rev Revision) revisionGroup {
|
||||
@ -70,25 +73,34 @@ func groupRevisionsByMonth(revs []Revision) (res []revisionGroup) {
|
||||
return res
|
||||
}
|
||||
|
||||
// groupRevisionsByPeriodFromNow groups close-together revisions.
|
||||
// groupRevisionsByPeriodFromNow groups close-together revisions and returns the first changeGroupMaxSize (30) groups.
|
||||
// If two revisions happened within period of each other, they are put in the same group.
|
||||
func groupRevisionsByPeriod(revs []Revision, period time.Duration) (res []revisionGroup) {
|
||||
if len(revs) == 0 {
|
||||
func groupRevisionsByPeriod(revs recentChangesStream, period time.Duration) (res []revisionGroup) {
|
||||
nextRev := revs.iterator()
|
||||
rev, empty := nextRev()
|
||||
if empty {
|
||||
return res
|
||||
}
|
||||
|
||||
currTime := revs[0].Time
|
||||
currGroup := newRevisionGroup(revs[0])
|
||||
for _, rev := range revs[1:] {
|
||||
currTime := rev.Time
|
||||
currGroup := newRevisionGroup(rev)
|
||||
for {
|
||||
rev, done := nextRev()
|
||||
if done {
|
||||
return append(res, currGroup)
|
||||
}
|
||||
|
||||
if currTime.Sub(rev.Time) < period && currGroup[0].Username == rev.Username {
|
||||
currGroup.addRevision(rev)
|
||||
} else {
|
||||
res = append(res, currGroup)
|
||||
if len(res) == changeGroupMaxSize {
|
||||
return res
|
||||
}
|
||||
currGroup = newRevisionGroup(rev)
|
||||
}
|
||||
currTime = rev.Time
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (grp revisionGroup) feedItem(opts FeedOptions) feeds.Item {
|
||||
@ -154,7 +166,7 @@ func ParseFeedOptions(query url.Values) (FeedOptions, error) {
|
||||
}
|
||||
|
||||
type FeedGrouping interface {
|
||||
Group([]Revision) []revisionGroup
|
||||
Group(recentChangesStream) []revisionGroup
|
||||
}
|
||||
|
||||
func parseFeedGrouping(query url.Values) (FeedGrouping, error) {
|
||||
@ -171,8 +183,8 @@ func parseFeedGrouping(query url.Values) (FeedGrouping, error) {
|
||||
|
||||
type NormalFeedGrouping struct{}
|
||||
|
||||
func (NormalFeedGrouping) Group(revs []Revision) (res []revisionGroup) {
|
||||
for _, rev := range revs {
|
||||
func (NormalFeedGrouping) Group(revs recentChangesStream) (res []revisionGroup) {
|
||||
for _, rev := range revs.next(changeGroupMaxSize) {
|
||||
res = append(res, newRevisionGroup(rev))
|
||||
}
|
||||
return res
|
||||
@ -182,7 +194,7 @@ type PeriodFeedGrouping struct {
|
||||
Period time.Duration
|
||||
}
|
||||
|
||||
func (g PeriodFeedGrouping) Group(revs []Revision) (res []revisionGroup) {
|
||||
func (g PeriodFeedGrouping) Group(revs recentChangesStream) (res []revisionGroup) {
|
||||
return groupRevisionsByPeriod(revs, g.Period)
|
||||
}
|
||||
|
||||
@ -195,8 +207,7 @@ const (
|
||||
|
||||
func parseFeedGroupOrder(query url.Values) (FeedGroupOrder, error) {
|
||||
switch query.Get("order") {
|
||||
case "old-to-new":
|
||||
case "":
|
||||
case "", "old-to-new":
|
||||
return OldToNew, nil
|
||||
case "new-to-old":
|
||||
return NewToOld, nil
|
||||
|
@ -21,53 +21,96 @@ type Revision struct {
|
||||
hyphaeAffectedBuf []string
|
||||
}
|
||||
|
||||
// gitLog calls `git log` and parses the results.
|
||||
func gitLog(args ...string) ([]Revision, error) {
|
||||
args = append([]string{
|
||||
"log", "--abbrev-commit", "--no-merges",
|
||||
"--pretty=format:%h\t%ae\t%at\t%s",
|
||||
}, args...)
|
||||
out, err := silentGitsh(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outStr := out.String()
|
||||
if outStr == "" {
|
||||
// if there are no commits to return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var revs []Revision
|
||||
for _, line := range strings.Split(outStr, "\n") {
|
||||
revs = append(revs, parseRevisionLine(line))
|
||||
}
|
||||
return revs, nil
|
||||
}
|
||||
|
||||
type recentChangesStream struct {
|
||||
currHash string
|
||||
}
|
||||
|
||||
func newRecentChangesStream() recentChangesStream {
|
||||
// next returns the next n revisions from the stream, ordered most recent first.
|
||||
// If there are less than n revisions remaining, it will return only those.
|
||||
return recentChangesStream{currHash: ""}
|
||||
}
|
||||
|
||||
func (stream *recentChangesStream) next(n int) []Revision {
|
||||
args := []string{"--max-count=" + strconv.Itoa(n)}
|
||||
if stream.currHash == "" {
|
||||
args = append(args, "HEAD")
|
||||
} else {
|
||||
// currHash is the last revision from the last call, so skip it
|
||||
args = append(args, "--skip=1", stream.currHash)
|
||||
}
|
||||
// I don't think this can fail, so ignore the error
|
||||
res, _ := gitLog(args...)
|
||||
if len(res) != 0 {
|
||||
stream.currHash = res[len(res)-1].Hash
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// recentChangesIterator returns a function that returns successive revisions from the stream.
|
||||
// It buffers revisions to avoid calling git every time.
|
||||
func (stream recentChangesStream) iterator() func() (Revision, bool) {
|
||||
var buf []Revision
|
||||
return func() (Revision, bool) {
|
||||
if len(buf) == 0 {
|
||||
// no real reason to choose 30, just needs some large number
|
||||
buf = stream.next(30)
|
||||
if len(buf) == 0 {
|
||||
// revs has no revisions left
|
||||
return Revision{}, true
|
||||
}
|
||||
}
|
||||
rev := buf[0]
|
||||
buf = buf[1:]
|
||||
return rev, false
|
||||
}
|
||||
}
|
||||
|
||||
// RecentChanges gathers an arbitrary number of latest changes in form of revisions slice, ordered most recent first.
|
||||
func RecentChanges(n int) []Revision {
|
||||
var (
|
||||
out, err = silentGitsh(
|
||||
"log", "--oneline", "--no-merges",
|
||||
"--pretty=format:%h\t%ae\t%at\t%s",
|
||||
"--max-count="+strconv.Itoa(n),
|
||||
)
|
||||
revs []Revision
|
||||
)
|
||||
if err == nil {
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
revs = append(revs, parseRevisionLine(line))
|
||||
}
|
||||
}
|
||||
stream := newRecentChangesStream()
|
||||
revs := stream.next(n)
|
||||
log.Printf("Found %d recent changes", len(revs))
|
||||
return revs
|
||||
}
|
||||
|
||||
// Revisions returns slice of revisions for the given hypha name, ordered most recent first.
|
||||
func Revisions(hyphaName string) ([]Revision, error) {
|
||||
revs, err := gitLog("--", hyphaName+".*")
|
||||
log.Printf("Found %d revisions for ‘%s’\n", len(revs), hyphaName)
|
||||
return revs, err
|
||||
}
|
||||
|
||||
// FileChanged tells you if the file has been changed since the last commit.
|
||||
func FileChanged(path string) bool {
|
||||
_, err := gitsh("diff", "--exit-code", path)
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// Revisions returns slice of revisions for the given hypha name, ordered most recent first.
|
||||
func Revisions(hyphaName string) ([]Revision, error) {
|
||||
var (
|
||||
out, err = silentGitsh(
|
||||
"log", "--oneline", "--no-merges",
|
||||
// Hash, author email, author time, commit msg separated by tab
|
||||
"--pretty=format:%h\t%ae\t%at\t%s",
|
||||
"--", hyphaName+".*",
|
||||
)
|
||||
revs []Revision
|
||||
)
|
||||
if err == nil {
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
if line != "" {
|
||||
revs = append(revs, parseRevisionLine(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("Found %d revisions for ‘%s’\n", len(revs), hyphaName)
|
||||
return revs, err
|
||||
}
|
||||
|
||||
// Return time like dd — 13:42
|
||||
func (rev *Revision) timeToDisplay() string {
|
||||
D := rev.Time.Day()
|
||||
|
Loading…
Reference in New Issue
Block a user