+62
-61
knotserver/git/merge.go
+62
-61
knotserver/git/merge.go
···
12
12
"github.com/dgraph-io/ristretto"
13
13
"github.com/go-git/go-git/v5"
14
14
"github.com/go-git/go-git/v5/plumbing"
15
-
"tangled.sh/tangled.sh/core/patchutil"
16
15
)
17
16
18
17
type MergeCheckCache struct {
···
143
142
return tmpDir, nil
144
143
}
145
144
146
-
func (g *GitRepo) applyPatch(tmpDir, patchFile string, checkOnly bool, opts *MergeOptions) error {
145
+
func (g *GitRepo) checkPatch(tmpDir, patchFile string) error {
146
+
var stderr bytes.Buffer
147
+
148
+
cmd := exec.Command("git", "-C", tmpDir, "apply", "--check", "-v", patchFile)
149
+
cmd.Stderr = &stderr
150
+
151
+
if err := cmd.Run(); err != nil {
152
+
conflicts := parseGitApplyErrors(stderr.String())
153
+
return &ErrMerge{
154
+
Message: "patch cannot be applied cleanly",
155
+
Conflicts: conflicts,
156
+
HasConflict: len(conflicts) > 0,
157
+
OtherError: err,
158
+
}
159
+
}
160
+
return nil
161
+
}
162
+
163
+
func (g *GitRepo) applyPatch(tmpDir, patchFile string, opts *MergeOptions) error {
147
164
var stderr bytes.Buffer
148
165
var cmd *exec.Cmd
149
166
150
-
if checkOnly {
151
-
cmd = exec.Command("git", "-C", tmpDir, "apply", "--check", "-v", patchFile)
152
-
} else {
153
-
// if patch is a format-patch, apply using 'git am'
154
-
if opts.FormatPatch {
155
-
amCmd := exec.Command("git", "-C", tmpDir, "am", patchFile)
156
-
amCmd.Stderr = &stderr
157
-
if err := amCmd.Run(); err != nil {
158
-
return fmt.Errorf("patch application failed: %s", stderr.String())
159
-
}
160
-
return nil
167
+
// if patch is a format-patch, apply using 'git am'
168
+
if opts.FormatPatch {
169
+
amCmd := exec.Command("git", "-C", tmpDir, "am", patchFile)
170
+
amCmd.Stderr = &stderr
171
+
if err := amCmd.Run(); err != nil {
172
+
return fmt.Errorf("patch application failed: %s", stderr.String())
161
173
}
174
+
return nil
175
+
}
162
176
163
-
// else, apply using 'git apply' and commit it manually
164
-
exec.Command("git", "-C", tmpDir, "config", "advice.mergeConflict", "false").Run()
165
-
if opts != nil {
166
-
applyCmd := exec.Command("git", "-C", tmpDir, "apply", patchFile)
167
-
applyCmd.Stderr = &stderr
168
-
if err := applyCmd.Run(); err != nil {
169
-
return fmt.Errorf("patch application failed: %s", stderr.String())
170
-
}
177
+
// else, apply using 'git apply' and commit it manually
178
+
exec.Command("git", "-C", tmpDir, "config", "advice.mergeConflict", "false").Run()
179
+
if opts != nil {
180
+
applyCmd := exec.Command("git", "-C", tmpDir, "apply", patchFile)
181
+
applyCmd.Stderr = &stderr
182
+
if err := applyCmd.Run(); err != nil {
183
+
return fmt.Errorf("patch application failed: %s", stderr.String())
184
+
}
171
185
172
-
stageCmd := exec.Command("git", "-C", tmpDir, "add", ".")
173
-
if err := stageCmd.Run(); err != nil {
174
-
return fmt.Errorf("failed to stage changes: %w", err)
175
-
}
186
+
stageCmd := exec.Command("git", "-C", tmpDir, "add", ".")
187
+
if err := stageCmd.Run(); err != nil {
188
+
return fmt.Errorf("failed to stage changes: %w", err)
189
+
}
176
190
177
-
commitArgs := []string{"-C", tmpDir, "commit"}
191
+
commitArgs := []string{"-C", tmpDir, "commit"}
178
192
179
-
// Set author if provided
180
-
authorName := opts.AuthorName
181
-
authorEmail := opts.AuthorEmail
193
+
// Set author if provided
194
+
authorName := opts.AuthorName
195
+
authorEmail := opts.AuthorEmail
182
196
183
-
if authorEmail == "" {
184
-
authorEmail = "noreply@tangled.sh"
185
-
}
197
+
if authorEmail == "" {
198
+
authorEmail = "noreply@tangled.sh"
199
+
}
186
200
187
-
if authorName == "" {
188
-
authorName = "Tangled"
189
-
}
201
+
if authorName == "" {
202
+
authorName = "Tangled"
203
+
}
190
204
191
-
if authorName != "" {
192
-
commitArgs = append(commitArgs, "--author", fmt.Sprintf("%s <%s>", authorName, authorEmail))
193
-
}
205
+
if authorName != "" {
206
+
commitArgs = append(commitArgs, "--author", fmt.Sprintf("%s <%s>", authorName, authorEmail))
207
+
}
194
208
195
-
commitArgs = append(commitArgs, "-m", opts.CommitMessage)
209
+
commitArgs = append(commitArgs, "-m", opts.CommitMessage)
196
210
197
-
if opts.CommitBody != "" {
198
-
commitArgs = append(commitArgs, "-m", opts.CommitBody)
199
-
}
211
+
if opts.CommitBody != "" {
212
+
commitArgs = append(commitArgs, "-m", opts.CommitBody)
213
+
}
200
214
201
-
cmd = exec.Command("git", commitArgs...)
202
-
} else {
203
-
// If no commit message specified, use git-am which automatically creates a commit
204
-
cmd = exec.Command("git", "-C", tmpDir, "am", patchFile)
205
-
}
215
+
cmd = exec.Command("git", commitArgs...)
216
+
} else {
217
+
// If no commit message specified, use git-am which automatically creates a commit
218
+
cmd = exec.Command("git", "-C", tmpDir, "am", patchFile)
206
219
}
207
220
208
221
cmd.Stderr = &stderr
209
222
210
223
if err := cmd.Run(); err != nil {
211
-
if checkOnly {
212
-
conflicts := parseGitApplyErrors(stderr.String())
213
-
return &ErrMerge{
214
-
Message: "patch cannot be applied cleanly",
215
-
Conflicts: conflicts,
216
-
HasConflict: len(conflicts) > 0,
217
-
OtherError: err,
218
-
}
219
-
}
220
224
return fmt.Errorf("patch application failed: %s", stderr.String())
221
225
}
222
226
···
228
232
return val
229
233
}
230
234
231
-
var opts MergeOptions
232
-
opts.FormatPatch = patchutil.IsFormatPatch(string(patchData))
233
-
234
235
patchFile, err := g.createTempFileWithPatch(patchData)
235
236
if err != nil {
236
237
return &ErrMerge{
···
249
250
}
250
251
defer os.RemoveAll(tmpDir)
251
252
252
-
result := g.applyPatch(tmpDir, patchFile, true, &opts)
253
+
result := g.checkPatch(tmpDir, patchFile)
253
254
mergeCheckCache.Set(g, patchData, targetBranch, result)
254
255
return result
255
256
}
···
277
278
}
278
279
defer os.RemoveAll(tmpDir)
279
280
280
-
if err := g.applyPatch(tmpDir, patchFile, false, opts); err != nil {
281
+
if err := g.applyPatch(tmpDir, patchFile, opts); err != nil {
281
282
return err
282
283
}
283
284