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"
32 type TopicsCache map[string][]plumbing.Hash
34 type TopicsCacheState struct {
39 func (tc TopicsCache) Topics() []string {
40 topics := make([]string, 0, len(tc))
41 for t, _ := range tc {
42 topics = append(topics, t)
48 func getTopicsCache(cfg *Cfg, repoLog object.CommitIter) (TopicsCache, error) {
49 cache := TopicsCache(make(map[string][]plumbing.Hash))
50 if topicsTree == nil {
53 top := topicsRef.Hash()
55 if cfg.TopicsCachePath != "" {
56 fd, err := os.Open(cfg.TopicsCachePath)
60 dec := gob.NewDecoder(fd)
61 var cacheState TopicsCacheState
62 err = dec.Decode(&cacheState)
67 if cacheState.Top == top {
68 return cacheState.Cache, nil
74 commit, err := repoLog.Next()
78 for _, topic := range parseTopics(getNote(topicsTree, commit.Hash)) {
79 cache[topic] = append(cache[topic], commit.Hash)
83 if cfg.TopicsCachePath != "" {
84 // Assume that probability of suffix collision is negligible
85 suffix := strconv.FormatInt(time.Now().UnixNano()+int64(os.Getpid()), 16)
86 tmpPath := cfg.TopicsCachePath + suffix
87 fd, err := os.OpenFile(
89 os.O_RDWR|os.O_CREATE|os.O_EXCL,
95 enc := gob.NewEncoder(fd)
96 err = enc.Encode(&TopicsCacheState{top, cache})
102 if err = fd.Sync(); err != nil {
107 if err = fd.Close(); err != nil {
111 if err = os.Rename(tmpPath, cfg.TopicsCachePath); err != nil {
120 type HashesIter struct {
121 hashes []plumbing.Hash
124 func (s *HashesIter) Next() (*object.Commit, error) {
125 if len(s.hashes) == 0 {
129 h, s.hashes = s.hashes[0], s.hashes[1:]
130 return repo.CommitObject(h)