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