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/bug"
 10	"github.com/MichaelMure/git-bug/entity"
 11	"github.com/MichaelMure/git-bug/identity"
 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 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		results := identity.MergeAll(c.repo, remote)
 99		for result := range results {
100			out <- result
101
102			if result.Err != nil {
103				continue
104			}
105
106			switch result.Status {
107			case entity.MergeStatusNew, entity.MergeStatusUpdated:
108				i := result.Entity.(*identity.Identity)
109				c.muIdentity.Lock()
110				c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
111				c.muIdentity.Unlock()
112			}
113		}
114
115		results = bug.MergeAll(c.repo, remote)
116		for result := range results {
117			out <- result
118
119			if result.Err != nil {
120				continue
121			}
122
123			switch result.Status {
124			case entity.MergeStatusNew, entity.MergeStatusUpdated:
125				b := result.Entity.(*bug.Bug)
126				snap := b.Compile()
127				c.muBug.Lock()
128				c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
129				c.muBug.Unlock()
130			}
131		}
132
133		err := c.write()
134
135		// No easy way out here ..
136		if err != nil {
137			panic(err)
138		}
139	}()
140
141	return out
142}
143
144// Push update a remote with the local changes
145func (c *RepoCache) Push(remote string) (string, error) {
146	stdout1, err := identity.Push(c.repo, remote)
147	if err != nil {
148		return stdout1, err
149	}
150
151	stdout2, err := bug.Push(c.repo, remote)
152	if err != nil {
153		return stdout2, err
154	}
155
156	return stdout1 + stdout2, nil
157}
158
159// Pull will do a Fetch + MergeAll
160// This function will return an error if a merge fail
161func (c *RepoCache) Pull(remote string) error {
162	_, err := c.Fetch(remote)
163	if err != nil {
164		return err
165	}
166
167	for merge := range c.MergeAll(remote) {
168		if merge.Err != nil {
169			return merge.Err
170		}
171		if merge.Status == entity.MergeStatusInvalid {
172			return errors.Errorf("merge failure: %s", merge.Reason)
173		}
174	}
175
176	return nil
177}
178
179func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
180	err := identity.SetUserIdentity(c.repo, i.Identity)
181	if err != nil {
182		return err
183	}
184
185	c.muIdentity.RLock()
186	defer c.muIdentity.RUnlock()
187
188	// Make sure that everything is fine
189	if _, ok := c.identities[i.Id()]; !ok {
190		panic("SetUserIdentity while the identity is not from the cache, something is wrong")
191	}
192
193	c.userIdentityId = i.Id()
194
195	return nil
196}
197
198func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
199	if c.userIdentityId != "" {
200		i, ok := c.identities[c.userIdentityId]
201		if ok {
202			return i, nil
203		}
204	}
205
206	c.muIdentity.Lock()
207	defer c.muIdentity.Unlock()
208
209	i, err := identity.GetUserIdentity(c.repo)
210	if err != nil {
211		return nil, err
212	}
213
214	cached := NewIdentityCache(c, i)
215	c.identities[i.Id()] = cached
216	c.userIdentityId = i.Id()
217
218	return cached, nil
219}
220
221func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
222	if c.userIdentityId == "" {
223		id, err := identity.GetUserIdentityId(c.repo)
224		if err != nil {
225			return nil, err
226		}
227		c.userIdentityId = id
228	}
229
230	c.muIdentity.RLock()
231	defer c.muIdentity.RUnlock()
232
233	excerpt, ok := c.identitiesExcerpts[c.userIdentityId]
234	if !ok {
235		return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId)
236	}
237	return excerpt, nil
238}
239
240func (c *RepoCache) IsUserIdentitySet() (bool, error) {
241	return identity.IsUserIdentitySet(c.repo)
242}