repo_cache_common.go

  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
 14// Name returns the registered name of this repository, or empty string for
 15// the default (unnamed) repository.
 16func (c *RepoCache) Name() string {
 17	if c.name == defaultRepoName {
 18		return ""
 19	}
 20	return c.name
 21}
 22
 23// GetPath returns the root directory path of the underlying git repository.
 24func (c *RepoCache) GetPath() string {
 25	return c.repo.GetPath()
 26}
 27
 28// LocalConfig give access to the repository scoped configuration
 29func (c *RepoCache) LocalConfig() repository.Config {
 30	return c.repo.LocalConfig()
 31}
 32
 33// GlobalConfig give access to the global scoped configuration
 34func (c *RepoCache) GlobalConfig() repository.Config {
 35	return c.repo.GlobalConfig()
 36}
 37
 38// AnyConfig give access to a merged local/global configuration
 39func (c *RepoCache) AnyConfig() repository.ConfigRead {
 40	return c.repo.AnyConfig()
 41}
 42
 43// Keyring give access to a user-wide storage for secrets
 44func (c *RepoCache) Keyring() repository.Keyring {
 45	return c.repo.Keyring()
 46}
 47
 48// GetUserName returns the name the user has used to configure git
 49func (c *RepoCache) GetUserName() (string, error) {
 50	return c.repo.GetUserName()
 51}
 52
 53// GetUserEmail returns the email address that the user has used to configure git.
 54func (c *RepoCache) GetUserEmail() (string, error) {
 55	return c.repo.GetUserEmail()
 56}
 57
 58// GetCoreEditor returns the name of the editor that the user has used to configure git.
 59func (c *RepoCache) GetCoreEditor() (string, error) {
 60	return c.repo.GetCoreEditor()
 61}
 62
 63// GetRemotes returns the configured remotes repositories.
 64func (c *RepoCache) GetRemotes() (map[string]string, error) {
 65	return c.repo.GetRemotes()
 66}
 67
 68// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
 69func (c *RepoCache) LocalStorage() repository.LocalStorage {
 70	return c.repo.LocalStorage()
 71}
 72
 73// ReadData will attempt to read arbitrary data from the given hash
 74func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
 75	return c.repo.ReadData(hash)
 76}
 77
 78// GetRepo returns the underlying repository for operations not covered by
 79// RepoCache (e.g. git object browsing). Callers may type-assert to
 80// repository.RepoBrowse for extended read-only access.
 81func (c *RepoCache) GetRepo() repository.ClockedRepo {
 82	return c.repo
 83}
 84
 85// StoreData will store arbitrary data and return the corresponding hash
 86func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
 87	return c.repo.StoreData(data)
 88}
 89
 90// Fetch retrieve updates from a remote
 91// This does not change the local bugs or identities state
 92func (c *RepoCache) Fetch(remote string) (string, error) {
 93	prefixes := make([]string, len(c.subcaches))
 94	for i, subcache := range c.subcaches {
 95		prefixes[i] = subcache.GetNamespace()
 96	}
 97
 98	// fetch everything at once, to have a single auth step if required.
 99	return c.repo.FetchRefs(remote, prefixes...)
100}
101
102// RemoveAll deletes all entities from the cache and the disk.
103func (c *RepoCache) RemoveAll() error {
104	var errWait multierr.ErrWaitGroup
105	for _, mgmt := range c.subcaches {
106		errWait.Go(mgmt.RemoveAll)
107	}
108	return errWait.Wait()
109}
110
111// MergeAll will merge all the available remote bug and identities
112func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
113	out := make(chan entity.MergeResult)
114
115	dependency := [][]cacheMgmt{
116		{c.identities},
117		{c.bugs},
118	}
119
120	// run MergeAll according to entities dependencies and merge the results
121	go func() {
122		defer close(out)
123
124		for _, subcaches := range dependency {
125			var wg sync.WaitGroup
126			for _, subcache := range subcaches {
127				wg.Add(1)
128				go func(subcache cacheMgmt) {
129					for res := range subcache.MergeAll(remote) {
130						out <- res
131					}
132					wg.Done()
133				}(subcache)
134			}
135			wg.Wait()
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	prefixes := make([]string, len(c.subcaches))
145	for i, subcache := range c.subcaches {
146		prefixes[i] = subcache.GetNamespace()
147	}
148
149	// push everything at once, to have a single auth step if required
150	return c.repo.PushRefs(remote, prefixes...)
151}
152
153// Pull will do a Fetch + MergeAll
154// This function will return an error if a merge fail
155func (c *RepoCache) Pull(remote string) error {
156	_, err := c.Fetch(remote)
157	if err != nil {
158		return err
159	}
160
161	for merge := range c.MergeAll(remote) {
162		if merge.Err != nil {
163			return merge.Err
164		}
165		if merge.Status == entity.MergeStatusInvalid {
166			return errors.Errorf("merge failure: %s", merge.Reason)
167		}
168	}
169
170	return nil
171}
172
173func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
174	c.muUserIdentity.RLock()
175	defer c.muUserIdentity.RUnlock()
176
177	// Make sure that everything is fine
178	if _, err := c.identities.Resolve(i.Id()); err != nil {
179		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
180	}
181
182	err := identity.SetUserIdentity(c.repo, i.Identity)
183	if err != nil {
184		return err
185	}
186
187	c.userIdentityId = i.Id()
188
189	return nil
190}
191
192func (c *RepoCache) ClearUserIdentity() error {
193	c.muUserIdentity.Lock()
194	defer c.muUserIdentity.Unlock()
195
196	err := identity.ClearUserIdentity(c.repo)
197	if err != nil {
198		return err
199	}
200
201	c.userIdentityId = ""
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}