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