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