1package cache
  2
  3import (
  4	"sync"
  5
  6	"github.com/pkg/errors"
  7
  8	"github.com/git-bug/git-bug/entities/identity"
  9	"github.com/git-bug/git-bug/entity"
 10	"github.com/git-bug/git-bug/repository"
 11	"github.com/git-bug/git-bug/util/multierr"
 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
 33// Keyring give access to a user-wide storage for secrets
 34func (c *RepoCache) Keyring() repository.Keyring {
 35	return c.repo.Keyring()
 36}
 37
 38// GetUserName returns the name the user has used to configure git
 39func (c *RepoCache) GetUserName() (string, error) {
 40	return c.repo.GetUserName()
 41}
 42
 43// GetUserEmail returns the email address that the user has used to configure git.
 44func (c *RepoCache) GetUserEmail() (string, error) {
 45	return c.repo.GetUserEmail()
 46}
 47
 48// GetCoreEditor returns the name of the editor that the user has used to configure git.
 49func (c *RepoCache) GetCoreEditor() (string, error) {
 50	return c.repo.GetCoreEditor()
 51}
 52
 53// GetRemotes returns the configured remotes repositories.
 54func (c *RepoCache) GetRemotes() (map[string]string, error) {
 55	return c.repo.GetRemotes()
 56}
 57
 58// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
 59func (c *RepoCache) LocalStorage() repository.LocalStorage {
 60	return c.repo.LocalStorage()
 61}
 62
 63// ReadData will attempt to read arbitrary data from the given hash
 64func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
 65	return c.repo.ReadData(hash)
 66}
 67
 68// StoreData will store arbitrary data and return the corresponding hash
 69func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
 70	return c.repo.StoreData(data)
 71}
 72
 73// Fetch retrieve updates from a remote
 74// This does not change the local bugs or identities state
 75func (c *RepoCache) Fetch(remote string) (string, error) {
 76	prefixes := make([]string, len(c.subcaches))
 77	for i, subcache := range c.subcaches {
 78		prefixes[i] = subcache.GetNamespace()
 79	}
 80
 81	// fetch everything at once, to have a single auth step if required.
 82	return c.repo.FetchRefs(remote, prefixes...)
 83}
 84
 85// RemoveAll deletes all entities from the cache and the disk.
 86func (c *RepoCache) RemoveAll() error {
 87	var errWait multierr.ErrWaitGroup
 88	for _, mgmt := range c.subcaches {
 89		errWait.Go(mgmt.RemoveAll)
 90	}
 91	return errWait.Wait()
 92}
 93
 94// MergeAll will merge all the available remote bug and identities
 95func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
 96	out := make(chan entity.MergeResult)
 97
 98	dependency := [][]cacheMgmt{
 99		{c.identities},
100		{c.bugs},
101	}
102
103	// run MergeAll according to entities dependencies and merge the results
104	go func() {
105		defer close(out)
106
107		for _, subcaches := range dependency {
108			var wg sync.WaitGroup
109			for _, subcache := range subcaches {
110				wg.Add(1)
111				go func(subcache cacheMgmt) {
112					for res := range subcache.MergeAll(remote) {
113						out <- res
114					}
115					wg.Done()
116				}(subcache)
117			}
118			wg.Wait()
119		}
120	}()
121
122	return out
123}
124
125// Push update a remote with the local changes
126func (c *RepoCache) Push(remote string) (string, error) {
127	prefixes := make([]string, len(c.subcaches))
128	for i, subcache := range c.subcaches {
129		prefixes[i] = subcache.GetNamespace()
130	}
131
132	// push everything at once, to have a single auth step if required
133	return c.repo.PushRefs(remote, prefixes...)
134}
135
136// Pull will do a Fetch + MergeAll
137// This function will return an error if a merge fail
138func (c *RepoCache) Pull(remote string) error {
139	_, err := c.Fetch(remote)
140	if err != nil {
141		return err
142	}
143
144	for merge := range c.MergeAll(remote) {
145		if merge.Err != nil {
146			return merge.Err
147		}
148		if merge.Status == entity.MergeStatusInvalid {
149			return errors.Errorf("merge failure: %s", merge.Reason)
150		}
151	}
152
153	return nil
154}
155
156func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
157	c.muUserIdentity.RLock()
158	defer c.muUserIdentity.RUnlock()
159
160	// Make sure that everything is fine
161	if _, err := c.identities.Resolve(i.Id()); err != nil {
162		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
163	}
164
165	err := identity.SetUserIdentity(c.repo, i.Identity)
166	if err != nil {
167		return err
168	}
169
170	c.userIdentityId = i.Id()
171
172	return nil
173}
174
175func (c *RepoCache) ClearUserIdentity() error {
176	c.muUserIdentity.Lock()
177	defer c.muUserIdentity.Unlock()
178
179	err := identity.ClearUserIdentity(c.repo)
180	if err != nil {
181		return err
182	}
183
184	c.userIdentityId = ""
185	return nil
186}
187
188func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
189	c.muUserIdentity.RLock()
190	if c.userIdentityId != "" {
191		defer c.muUserIdentity.RUnlock()
192		return c.identities.Resolve(c.userIdentityId)
193	}
194	c.muUserIdentity.RUnlock()
195
196	c.muUserIdentity.Lock()
197	defer c.muUserIdentity.Unlock()
198
199	i, err := identity.GetUserIdentityId(c.repo)
200	if err != nil {
201		return nil, err
202	}
203
204	c.userIdentityId = i
205
206	return c.identities.Resolve(i)
207}
208
209func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
210	c.muUserIdentity.RLock()
211	if c.userIdentityId != "" {
212		defer c.muUserIdentity.RUnlock()
213		return c.identities.ResolveExcerpt(c.userIdentityId)
214	}
215	c.muUserIdentity.RUnlock()
216
217	c.muUserIdentity.Lock()
218	defer c.muUserIdentity.Unlock()
219
220	i, err := identity.GetUserIdentityId(c.repo)
221	if err != nil {
222		return nil, err
223	}
224
225	c.userIdentityId = i
226
227	return c.identities.ResolveExcerpt(i)
228}
229
230func (c *RepoCache) IsUserIdentitySet() (bool, error) {
231	return identity.IsUserIdentitySet(c.repo)
232}