bug_actions.go

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