2 SGBlog -- Git-backed CGI/inetd blogging/phlogging engine
3 Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Affero General Public License as
7 published by the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 "github.com/go-git/go-git/v5/plumbing"
29 "github.com/go-git/go-git/v5/plumbing/object"
30 "go.stargrave.org/sgblog"
33 type TopicsCache map[string][]plumbing.Hash
35 type TopicsCacheState struct {
40 func (tc TopicsCache) Topics() []string {
41 topics := make([]string, 0, len(tc))
42 for t, _ := range tc {
43 topics = append(topics, t)
49 func getTopicsCache(cfg *Cfg, repoLog object.CommitIter) (TopicsCache, error) {
50 cache := TopicsCache(make(map[string][]plumbing.Hash))
51 if topicsTree == nil {
54 top := topicsRef.Hash()
56 if cfg.TopicsCachePath != "" {
57 fd, err := os.Open(cfg.TopicsCachePath)
61 dec := gob.NewDecoder(fd)
62 var cacheState TopicsCacheState
63 err = dec.Decode(&cacheState)
68 if cacheState.Top == top {
69 return cacheState.Cache, nil
75 commit, err := repoLog.Next()
79 for _, topic := range sgblog.ParseTopics(sgblog.GetNote(repo, topicsTree, commit.Hash)) {
80 cache[topic] = append(cache[topic], commit.Hash)
84 if cfg.TopicsCachePath != "" {
85 // Assume that probability of suffix collision is negligible
86 suffix := strconv.FormatInt(time.Now().UnixNano()+int64(os.Getpid()), 16)
87 tmpPath := cfg.TopicsCachePath + suffix
88 fd, err := os.OpenFile(
90 os.O_RDWR|os.O_CREATE|os.O_EXCL,
96 enc := gob.NewEncoder(fd)
97 err = enc.Encode(&TopicsCacheState{top, cache})
103 if err = fd.Sync(); err != nil {
108 if err = fd.Close(); err != nil {
112 if err = os.Rename(tmpPath, cfg.TopicsCachePath); err != nil {
121 type HashesIter struct {
122 hashes []plumbing.Hash
125 func (s *HashesIter) Next() (*object.Commit, error) {
126 if len(s.hashes) == 0 {
130 h, s.hashes = s.hashes[0], s.hashes[1:]
131 return repo.CommitObject(h)