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
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
24func Push(repo repository.Repo, remote string) (string, error) {
25 return repo.PushRefs(remote, bugsRefPattern+"*")
26}
27
28func Pull(repo repository.Repo, out io.Writer, remote string) error {
29 if out == nil {
30 out = ioutil.Discard
31 }
32
33 fmt.Fprintf(out, "Fetching remote ...\n")
34
35 stdout, err := Fetch(repo, remote)
36 if err != nil {
37 return err
38 }
39
40 out.Write([]byte(stdout))
41
42 fmt.Fprintf(out, "Merging data ...\n")
43
44 for merge := range MergeAll(repo, remote) {
45 if merge.Err != nil {
46 return merge.Err
47 }
48
49 if merge.Status != MsgMergeNothing {
50 fmt.Fprintf(out, "%s: %s\n", merge.HumanId, merge.Status)
51 }
52 }
53
54 return nil
55}
56
57type MergeResult struct {
58 Err error
59
60 Id string
61 HumanId string
62 Status string
63}
64
65func newMergeError(id string, err error) MergeResult {
66 return MergeResult{
67 Id: id,
68 HumanId: formatHumanId(id),
69 Status: err.Error(),
70 }
71}
72
73func newMergeStatus(id string, status string) MergeResult {
74 return MergeResult{
75 Id: id,
76 HumanId: formatHumanId(id),
77 Status: status,
78 }
79}
80
81func MergeAll(repo repository.Repo, remote string) <-chan MergeResult {
82 out := make(chan MergeResult)
83
84 go func() {
85 defer close(out)
86
87 remoteRefSpec := fmt.Sprintf(bugsRemoteRefPattern, remote)
88 remoteRefs, err := repo.ListRefs(remoteRefSpec)
89
90 if err != nil {
91 out <- MergeResult{Err: err}
92 return
93 }
94
95 for _, remoteRef := range remoteRefs {
96 refSplitted := strings.Split(remoteRef, "/")
97 id := refSplitted[len(refSplitted)-1]
98
99 remoteBug, err := readBug(repo, remoteRef)
100
101 if err != nil {
102 out <- newMergeError(id, err)
103 continue
104 }
105
106 // Check for error in remote data
107 if !remoteBug.IsValid() {
108 out <- newMergeStatus(id, MsgMergeInvalid)
109 continue
110 }
111
112 localRef := bugsRefPattern + remoteBug.Id()
113 localExist, err := repo.RefExist(localRef)
114
115 if err != nil {
116 out <- newMergeError(id, err)
117 continue
118 }
119
120 // the bug is not local yet, simply create the reference
121 if !localExist {
122 err := repo.CopyRef(remoteRef, localRef)
123
124 if err != nil {
125 out <- newMergeError(id, err)
126 return
127 }
128
129 out <- newMergeStatus(id, MsgMergeNew)
130 continue
131 }
132
133 localBug, err := readBug(repo, localRef)
134
135 if err != nil {
136 out <- newMergeError(id, err)
137 return
138 }
139
140 updated, err := localBug.Merge(repo, remoteBug)
141
142 if err != nil {
143 out <- newMergeError(id, err)
144 return
145 }
146
147 if updated {
148 out <- newMergeStatus(id, MsgMergeUpdated)
149 } else {
150 out <- newMergeStatus(id, MsgMergeNothing)
151 }
152 }
153 }()
154
155 return out
156}