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