repo_cache_common.go

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