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 err := remoteBug.Validate(); err != nil {
 70				out <- newMergeInvalidStatus(id, err.Error())
 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
131type MergeResult struct {
132	// Err is set when a terminal error occur in the process
133	Err error
134
135	Id     string
136	Status MergeStatus
137
138	// Only set for invalid status
139	Reason string
140
141	// Not set for invalid status
142	Bug *Bug
143}
144
145func (mr MergeResult) String() string {
146	switch mr.Status {
147	case MergeStatusNew:
148		return "new"
149	case MergeStatusInvalid:
150		return fmt.Sprintf("invalid data: %s", mr.Reason)
151	case MergeStatusUpdated:
152		return "updated"
153	case MergeStatusNothing:
154		return "nothing to do"
155	default:
156		panic("unknown merge status")
157	}
158}
159
160func newMergeError(err error, id string) MergeResult {
161	return MergeResult{
162		Err: err,
163		Id:  id,
164	}
165}
166
167func newMergeStatus(status MergeStatus, id string, bug *Bug) MergeResult {
168	return MergeResult{
169		Id:     id,
170		Status: status,
171
172		// Bug is not set for an invalid merge result
173		Bug: bug,
174	}
175}
176
177func newMergeInvalidStatus(id string, reason string) MergeResult {
178	return MergeResult{
179		Id:     id,
180		Status: MergeStatusInvalid,
181		Reason: reason,
182	}
183}