bug_actions.go

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