repo_cache_common.go

  1package cache
  2
  3import (
  4	"path/filepath"
  5	"sync"
  6
  7	"github.com/pkg/errors"
  8
  9	"github.com/git-bug/git-bug/entities/identity"
 10	"github.com/git-bug/git-bug/entity"
 11	"github.com/git-bug/git-bug/repository"
 12	"github.com/git-bug/git-bug/util/multierr"
 13)
 14
 15func (c *RepoCache) Name() string {
 16	return c.name
 17}
 18
 19// GetPath returns the root directory path of the underlying git repository.
 20func (c *RepoCache) GetPath() string {
 21	return c.repo.GetPath()
 22}
 23
 24// Slug returns a URL-friendly identifier for this repository.
 25// Named repos use their registered name. The default ("__default") repo
 26// derives its slug from the last component of the filesystem path so
 27// URLs look like /git-bug/issues rather than /__default/issues.
 28func (c *RepoCache) Slug() string {
 29	if c.name != defaultRepoName {
 30		return c.name
 31	}
 32	path := c.repo.GetPath()
 33	if path == "" {
 34		return c.name
 35	}
 36	return filepath.Base(path)
 37}
 38
 39// LocalConfig give access to the repository scoped configuration
 40func (c *RepoCache) LocalConfig() repository.Config {
 41	return c.repo.LocalConfig()
 42}
 43
 44// GlobalConfig give access to the global scoped configuration
 45func (c *RepoCache) GlobalConfig() repository.Config {
 46	return c.repo.GlobalConfig()
 47}
 48
 49// AnyConfig give access to a merged local/global configuration
 50func (c *RepoCache) AnyConfig() repository.ConfigRead {
 51	return c.repo.AnyConfig()
 52}
 53
 54// Keyring give access to a user-wide storage for secrets
 55func (c *RepoCache) Keyring() repository.Keyring {
 56	return c.repo.Keyring()
 57}
 58
 59// GetUserName returns the name the user has used to configure git
 60func (c *RepoCache) GetUserName() (string, error) {
 61	return c.repo.GetUserName()
 62}
 63
 64// GetUserEmail returns the email address that the user has used to configure git.
 65func (c *RepoCache) GetUserEmail() (string, error) {
 66	return c.repo.GetUserEmail()
 67}
 68
 69// GetCoreEditor returns the name of the editor that the user has used to configure git.
 70func (c *RepoCache) GetCoreEditor() (string, error) {
 71	return c.repo.GetCoreEditor()
 72}
 73
 74// GetRemotes returns the configured remotes repositories.
 75func (c *RepoCache) GetRemotes() (map[string]string, error) {
 76	return c.repo.GetRemotes()
 77}
 78
 79// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
 80func (c *RepoCache) LocalStorage() repository.LocalStorage {
 81	return c.repo.LocalStorage()
 82}
 83
 84// ReadData will attempt to read arbitrary data from the given hash
 85func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
 86	return c.repo.ReadData(hash)
 87}
 88
 89// GetRepo returns the underlying repository for operations not covered by
 90// RepoCache (e.g. git object browsing). Callers may type-assert to
 91// repository.RepoBrowse for extended read-only access.
 92func (c *RepoCache) GetRepo() repository.ClockedRepo {
 93	return c.repo
 94}
 95
 96// StoreData will store arbitrary data and return the corresponding hash
 97func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
 98	return c.repo.StoreData(data)
 99}
100
101// Fetch retrieve updates from a remote
102// This does not change the local bugs or identities state
103func (c *RepoCache) Fetch(remote string) (string, error) {
104	prefixes := make([]string, len(c.subcaches))
105	for i, subcache := range c.subcaches {
106		prefixes[i] = subcache.GetNamespace()
107	}
108
109	// fetch everything at once, to have a single auth step if required.
110	return c.repo.FetchRefs(remote, prefixes...)
111}
112
113// RemoveAll deletes all entities from the cache and the disk.
114func (c *RepoCache) RemoveAll() error {
115	var errWait multierr.ErrWaitGroup
116	for _, mgmt := range c.subcaches {
117		errWait.Go(mgmt.RemoveAll)
118	}
119	return errWait.Wait()
120}
121
122// MergeAll will merge all the available remote bug and identities
123func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
124	out := make(chan entity.MergeResult)
125
126	dependency := [][]cacheMgmt{
127		{c.identities},
128		{c.bugs},
129	}
130
131	// run MergeAll according to entities dependencies and merge the results
132	go func() {
133		defer close(out)
134
135		for _, subcaches := range dependency {
136			var wg sync.WaitGroup
137			for _, subcache := range subcaches {
138				wg.Add(1)
139				go func(subcache cacheMgmt) {
140					for res := range subcache.MergeAll(remote) {
141						out <- res
142					}
143					wg.Done()
144				}(subcache)
145			}
146			wg.Wait()
147		}
148	}()
149
150	return out
151}
152
153// Push update a remote with the local changes
154func (c *RepoCache) Push(remote string) (string, error) {
155	prefixes := make([]string, len(c.subcaches))
156	for i, subcache := range c.subcaches {
157		prefixes[i] = subcache.GetNamespace()
158	}
159
160	// push everything at once, to have a single auth step if required
161	return c.repo.PushRefs(remote, prefixes...)
162}
163
164// Pull will do a Fetch + MergeAll
165// This function will return an error if a merge fail
166func (c *RepoCache) Pull(remote string) error {
167	_, err := c.Fetch(remote)
168	if err != nil {
169		return err
170	}
171
172	for merge := range c.MergeAll(remote) {
173		if merge.Err != nil {
174			return merge.Err
175		}
176		if merge.Status == entity.MergeStatusInvalid {
177			return errors.Errorf("merge failure: %s", merge.Reason)
178		}
179	}
180
181	return nil
182}
183
184func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
185	c.muUserIdentity.RLock()
186	defer c.muUserIdentity.RUnlock()
187
188	// Make sure that everything is fine
189	if _, err := c.identities.Resolve(i.Id()); err != nil {
190		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
191	}
192
193	err := identity.SetUserIdentity(c.repo, i.Identity)
194	if err != nil {
195		return err
196	}
197
198	c.userIdentityId = i.Id()
199
200	return nil
201}
202
203func (c *RepoCache) ClearUserIdentity() error {
204	c.muUserIdentity.Lock()
205	defer c.muUserIdentity.Unlock()
206
207	err := identity.ClearUserIdentity(c.repo)
208	if err != nil {
209		return err
210	}
211
212	c.userIdentityId = ""
213	return nil
214}
215
216func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
217	c.muUserIdentity.RLock()
218	if c.userIdentityId != "" {
219		defer c.muUserIdentity.RUnlock()
220		return c.identities.Resolve(c.userIdentityId)
221	}
222	c.muUserIdentity.RUnlock()
223
224	c.muUserIdentity.Lock()
225	defer c.muUserIdentity.Unlock()
226
227	i, err := identity.GetUserIdentityId(c.repo)
228	if err != nil {
229		return nil, err
230	}
231
232	c.userIdentityId = i
233
234	return c.identities.Resolve(i)
235}
236
237func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
238	c.muUserIdentity.RLock()
239	if c.userIdentityId != "" {
240		defer c.muUserIdentity.RUnlock()
241		return c.identities.ResolveExcerpt(c.userIdentityId)
242	}
243	c.muUserIdentity.RUnlock()
244
245	c.muUserIdentity.Lock()
246	defer c.muUserIdentity.Unlock()
247
248	i, err := identity.GetUserIdentityId(c.repo)
249	if err != nil {
250		return nil, err
251	}
252
253	c.userIdentityId = i
254
255	return c.identities.ResolveExcerpt(i)
256}
257
258func (c *RepoCache) IsUserIdentitySet() (bool, error) {
259	return identity.IsUserIdentitySet(c.repo)
260}