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 // the bug is not local yet, simply create the reference
111 if !localExist {
112 err := repo.CopyRef(remoteRef, localRef)
113
114 if err != nil {
115 out <- newMergeError(id, err)
116 return
117 }
118
119 out <- newMergeStatus(id, MsgMergeNew)
120 continue
121 }
122
123 localBug, err := readBug(repo, localRef)
124
125 if err != nil {
126 out <- newMergeError(id, err)
127 return
128 }
129
130 updated, err := localBug.Merge(repo, remoteBug)
131
132 if err != nil {
133 out <- newMergeError(id, err)
134 return
135 }
136
137 if updated {
138 out <- newMergeStatus(id, MsgMergeUpdated)
139 } else {
140 out <- newMergeStatus(id, MsgMergeNothing)
141 }
142 }
143 }()
144
145 return out
146}