repo_cache_common.go

  1package cache
  2
  3import (
  4	"github.com/go-git/go-billy/v5"
  5	"github.com/pkg/errors"
  6
  7	"github.com/MichaelMure/git-bug/entities/bug"
  8	"github.com/MichaelMure/git-bug/entities/identity"
  9	"github.com/MichaelMure/git-bug/entity"
 10	"github.com/MichaelMure/git-bug/repository"
 11)
 12
 13func (c *RepoCache) Name() string {
 14	return c.name
 15}
 16
 17// LocalConfig give access to the repository scoped configuration
 18func (c *RepoCache) LocalConfig() repository.Config {
 19	return c.repo.LocalConfig()
 20}
 21
 22// GlobalConfig give access to the global scoped configuration
 23func (c *RepoCache) GlobalConfig() repository.Config {
 24	return c.repo.GlobalConfig()
 25}
 26
 27// AnyConfig give access to a merged local/global configuration
 28func (c *RepoCache) AnyConfig() repository.ConfigRead {
 29	return c.repo.AnyConfig()
 30}
 31
 32// Keyring give access to a user-wide storage for secrets
 33func (c *RepoCache) Keyring() repository.Keyring {
 34	return c.repo.Keyring()
 35}
 36
 37// GetUserName returns the name the user has used to configure git
 38func (c *RepoCache) GetUserName() (string, error) {
 39	return c.repo.GetUserName()
 40}
 41
 42// GetUserEmail returns the email address that the user has used to configure git.
 43func (c *RepoCache) GetUserEmail() (string, error) {
 44	return c.repo.GetUserEmail()
 45}
 46
 47// GetCoreEditor returns the name of the editor that the user has used to configure git.
 48func (c *RepoCache) GetCoreEditor() (string, error) {
 49	return c.repo.GetCoreEditor()
 50}
 51
 52// GetRemotes returns the configured remotes repositories.
 53func (c *RepoCache) GetRemotes() (map[string]string, error) {
 54	return c.repo.GetRemotes()
 55}
 56
 57// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
 58func (c *RepoCache) LocalStorage() billy.Filesystem {
 59	return c.repo.LocalStorage()
 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		author, err := c.GetUserIdentity()
 97		if err != nil {
 98			out <- entity.NewMergeError(err, "")
 99			return
100		}
101
102		results := identity.MergeAll(c.repo, remote)
103		for result := range results {
104			out <- result
105
106			if result.Err != nil {
107				continue
108			}
109
110			switch result.Status {
111			case entity.MergeStatusNew, entity.MergeStatusUpdated:
112				i := result.Entity.(*identity.Identity)
113				c.muIdentity.Lock()
114				c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
115				c.muIdentity.Unlock()
116			}
117		}
118
119		results = bug.MergeAll(c.repo, c.resolvers, remote, author)
120		for result := range results {
121			out <- result
122
123			if result.Err != nil {
124				continue
125			}
126
127			// TODO: have subcache do the merging?
128			switch result.Status {
129			case entity.MergeStatusNew:
130				b := result.Entity.(*bug.Bug)
131				_, err := c.bugs.add(b)
132			case entity.MergeStatusUpdated:
133				_, err := c.bugs.entityUpdated(b)
134				snap := b.Compile()
135				c.muBug.Lock()
136				c.bugExcerpts[result.Id] = NewBugExcerpt(b, snap)
137				c.muBug.Unlock()
138			}
139		}
140
141		err = c.write()
142		if err != nil {
143			out <- entity.NewMergeError(err, "")
144			return
145		}
146	}()
147
148	return out
149}
150
151// Push update a remote with the local changes
152func (c *RepoCache) Push(remote string) (string, error) {
153	stdout1, err := identity.Push(c.repo, remote)
154	if err != nil {
155		return stdout1, err
156	}
157
158	stdout2, err := bug.Push(c.repo, remote)
159	if err != nil {
160		return stdout2, err
161	}
162
163	return stdout1 + stdout2, nil
164}
165
166// Pull will do a Fetch + MergeAll
167// This function will return an error if a merge fail
168func (c *RepoCache) Pull(remote string) error {
169	_, err := c.Fetch(remote)
170	if err != nil {
171		return err
172	}
173
174	for merge := range c.MergeAll(remote) {
175		if merge.Err != nil {
176			return merge.Err
177		}
178		if merge.Status == entity.MergeStatusInvalid {
179			return errors.Errorf("merge failure: %s", merge.Reason)
180		}
181	}
182
183	return nil
184}
185
186func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
187	c.muUserIdentity.RLock()
188	defer c.muUserIdentity.RUnlock()
189
190	// Make sure that everything is fine
191	if _, err := c.identities.Resolve(i.Id()); err != nil {
192		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
193	}
194
195	err := identity.SetUserIdentity(c.repo, i.Identity)
196	if err != nil {
197		return err
198	}
199
200	c.userIdentityId = i.Id()
201
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}