bug_actions.go

  1package bug
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/MichaelMure/git-bug/repository"
  8)
  9
 10const MsgMergeNew = "new"
 11const MsgMergeInvalid = "invalid data"
 12const MsgMergeUpdated = "updated"
 13const MsgMergeNothing = "nothing to do"
 14
 15// Fetch retrieve update from a remote
 16// This does not change the local bugs state
 17func Fetch(repo repository.Repo, remote string) (string, error) {
 18	remoteRefSpec := fmt.Sprintf(bugsRemoteRefPattern, remote)
 19	fetchRefSpec := fmt.Sprintf("%s*:%s*", bugsRefPattern, remoteRefSpec)
 20
 21	return repo.FetchRefs(remote, fetchRefSpec)
 22}
 23
 24// Push update a remote with the local changes
 25func Push(repo repository.Repo, remote string) (string, error) {
 26	return repo.PushRefs(remote, bugsRefPattern+"*")
 27}
 28
 29// Pull will do a Fetch + MergeAll
 30// This function won't give details on the underlying process. If you need more
 31// use Fetch and MergeAll separately.
 32func Pull(repo repository.Repo, remote string) error {
 33	_, err := Fetch(repo, remote)
 34	if err != nil {
 35		return err
 36	}
 37
 38	for merge := range MergeAll(repo, remote) {
 39		if merge.Err != nil {
 40			return merge.Err
 41		}
 42	}
 43
 44	return nil
 45}
 46
 47type MergeResult struct {
 48	Err error
 49
 50	Id      string
 51	HumanId string
 52	Status  string
 53}
 54
 55func newMergeError(id string, err error) MergeResult {
 56	return MergeResult{
 57		Id:      id,
 58		HumanId: formatHumanId(id),
 59		Status:  err.Error(),
 60	}
 61}
 62
 63func newMergeStatus(id string, status string) MergeResult {
 64	return MergeResult{
 65		Id:      id,
 66		HumanId: formatHumanId(id),
 67		Status:  status,
 68	}
 69}
 70
 71func MergeAll(repo repository.Repo, remote string) <-chan MergeResult {
 72	out := make(chan MergeResult)
 73
 74	go func() {
 75		defer close(out)
 76
 77		remoteRefSpec := fmt.Sprintf(bugsRemoteRefPattern, remote)
 78		remoteRefs, err := repo.ListRefs(remoteRefSpec)
 79
 80		if err != nil {
 81			out <- MergeResult{Err: err}
 82			return
 83		}
 84
 85		for _, remoteRef := range remoteRefs {
 86			refSplitted := strings.Split(remoteRef, "/")
 87			id := refSplitted[len(refSplitted)-1]
 88
 89			remoteBug, err := readBug(repo, remoteRef)
 90
 91			if err != nil {
 92				out <- newMergeError(id, err)
 93				continue
 94			}
 95
 96			// Check for error in remote data
 97			if !remoteBug.IsValid() {
 98				out <- newMergeStatus(id, MsgMergeInvalid)
 99				continue
100			}
101
102			localRef := bugsRefPattern + remoteBug.Id()
103			localExist, err := repo.RefExist(localRef)
104
105			if err != nil {
106				out <- newMergeError(id, err)
107				continue
108			}
109
110			// the bug is not local yet, simply create the reference
111			if !localExist {
112				err := repo.CopyRef(remoteRef, localRef)
113
114				if err != nil {
115					out <- newMergeError(id, err)
116					return
117				}
118
119				out <- newMergeStatus(id, MsgMergeNew)
120				continue
121			}
122
123			localBug, err := readBug(repo, localRef)
124
125			if err != nil {
126				out <- newMergeError(id, err)
127				return
128			}
129
130			updated, err := localBug.Merge(repo, remoteBug)
131
132			if err != nil {
133				out <- newMergeError(id, err)
134				return
135			}
136
137			if updated {
138				out <- newMergeStatus(id, MsgMergeUpdated)
139			} else {
140				out <- newMergeStatus(id, MsgMergeNothing)
141			}
142		}
143	}()
144
145	return out
146}