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