1package cache
2
3import (
4 "fmt"
5
6 "github.com/go-git/go-billy/v5"
7 "github.com/pkg/errors"
8
9 "github.com/MichaelMure/git-bug/bug"
10 "github.com/MichaelMure/git-bug/entity"
11 "github.com/MichaelMure/git-bug/identity"
12 "github.com/MichaelMure/git-bug/repository"
13)
14
15func (c *RepoCache) Name() string {
16 return c.name
17}
18
19// LocalConfig give access to the repository scoped configuration
20func (c *RepoCache) LocalConfig() repository.Config {
21 return c.repo.LocalConfig()
22}
23
24// GlobalConfig give access to the global scoped configuration
25func (c *RepoCache) GlobalConfig() repository.Config {
26 return c.repo.GlobalConfig()
27}
28
29// AnyConfig give access to a merged local/global configuration
30func (c *RepoCache) AnyConfig() repository.ConfigRead {
31 return c.repo.AnyConfig()
32}
33
34// Keyring give access to a user-wide storage for secrets
35func (c *RepoCache) Keyring() repository.Keyring {
36 return c.repo.Keyring()
37}
38
39// GetUserName returns the name the the user has used to configure git
40func (c *RepoCache) GetUserName() (string, error) {
41 return c.repo.GetUserName()
42}
43
44// GetUserEmail returns the email address that the user has used to configure git.
45func (c *RepoCache) GetUserEmail() (string, error) {
46 return c.repo.GetUserEmail()
47}
48
49// GetCoreEditor returns the name of the editor that the user has used to configure git.
50func (c *RepoCache) GetCoreEditor() (string, error) {
51 return c.repo.GetCoreEditor()
52}
53
54// GetRemotes returns the configured remotes repositories.
55func (c *RepoCache) GetRemotes() (map[string]string, error) {
56 return c.repo.GetRemotes()
57}
58
59// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
60func (c *RepoCache) LocalStorage() billy.Filesystem {
61 return c.repo.LocalStorage()
62}
63
64// ReadData will attempt to read arbitrary data from the given hash
65func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
66 return c.repo.ReadData(hash)
67}
68
69// StoreData will store arbitrary data and return the corresponding hash
70func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
71 return c.repo.StoreData(data)
72}
73
74// Fetch retrieve updates from a remote
75// This does not change the local bugs or identities state
76func (c *RepoCache) Fetch(remote string) (string, error) {
77 stdout1, err := identity.Fetch(c.repo, remote)
78 if err != nil {
79 return stdout1, err
80 }
81
82 stdout2, err := bug.Fetch(c.repo, remote)
83 if err != nil {
84 return stdout2, err
85 }
86
87 return stdout1 + stdout2, nil
88}
89
90// MergeAll will merge all the available remote bug and identities
91func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
92 out := make(chan entity.MergeResult)
93
94 // Intercept merge results to update the cache properly
95 go func() {
96 defer close(out)
97
98 results := identity.MergeAll(c.repo, remote)
99 for result := range results {
100 out <- result
101
102 if result.Err != nil {
103 continue
104 }
105
106 switch result.Status {
107 case entity.MergeStatusNew, entity.MergeStatusUpdated:
108 i := result.Entity.(*identity.Identity)
109 c.muIdentity.Lock()
110 c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
111 c.muIdentity.Unlock()
112 }
113 }
114
115 results = bug.MergeAll(c.repo, remote)
116 for result := range results {
117 out <- result
118
119 if result.Err != nil {
120 continue
121 }
122
123 switch result.Status {
124 case entity.MergeStatusNew, entity.MergeStatusUpdated:
125 b := result.Entity.(*bug.Bug)
126 snap := b.Compile()
127 c.muBug.Lock()
128 c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
129 c.muBug.Unlock()
130 }
131 }
132
133 err := c.write()
134
135 // No easy way out here ..
136 if err != nil {
137 panic(err)
138 }
139 }()
140
141 return out
142}
143
144// Push update a remote with the local changes
145func (c *RepoCache) Push(remote string) (string, error) {
146 stdout1, err := identity.Push(c.repo, remote)
147 if err != nil {
148 return stdout1, err
149 }
150
151 stdout2, err := bug.Push(c.repo, remote)
152 if err != nil {
153 return stdout2, err
154 }
155
156 return stdout1 + stdout2, nil
157}
158
159// Pull will do a Fetch + MergeAll
160// This function will return an error if a merge fail
161func (c *RepoCache) Pull(remote string) error {
162 _, err := c.Fetch(remote)
163 if err != nil {
164 return err
165 }
166
167 for merge := range c.MergeAll(remote) {
168 if merge.Err != nil {
169 return merge.Err
170 }
171 if merge.Status == entity.MergeStatusInvalid {
172 return errors.Errorf("merge failure: %s", merge.Reason)
173 }
174 }
175
176 return nil
177}
178
179func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
180 err := identity.SetUserIdentity(c.repo, i.Identity)
181 if err != nil {
182 return err
183 }
184
185 c.muIdentity.RLock()
186 defer c.muIdentity.RUnlock()
187
188 // Make sure that everything is fine
189 if _, ok := c.identities[i.Id()]; !ok {
190 panic("SetUserIdentity while the identity is not from the cache, something is wrong")
191 }
192
193 c.userIdentityId = i.Id()
194
195 return nil
196}
197
198func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
199 if c.userIdentityId != "" {
200 i, ok := c.identities[c.userIdentityId]
201 if ok {
202 return i, nil
203 }
204 }
205
206 c.muIdentity.Lock()
207 defer c.muIdentity.Unlock()
208
209 i, err := identity.GetUserIdentity(c.repo)
210 if err != nil {
211 return nil, err
212 }
213
214 cached := NewIdentityCache(c, i)
215 c.identities[i.Id()] = cached
216 c.userIdentityId = i.Id()
217
218 return cached, nil
219}
220
221func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
222 if c.userIdentityId == "" {
223 id, err := identity.GetUserIdentityId(c.repo)
224 if err != nil {
225 return nil, err
226 }
227 c.userIdentityId = id
228 }
229
230 c.muIdentity.RLock()
231 defer c.muIdentity.RUnlock()
232
233 excerpt, ok := c.identitiesExcerpts[c.userIdentityId]
234 if !ok {
235 return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId)
236 }
237 return excerpt, nil
238}
239
240func (c *RepoCache) IsUserIdentitySet() (bool, error) {
241 return identity.IsUserIdentitySet(c.repo)
242}