Skip to content

Commit 7a428fa

Browse files
authored
Ensure that all unmerged files are merged when conflict checking (#20528)
There is a subtle bug in the code relating to collating the results of `git ls-files -u -z` in `unmergedFiles()`. The code here makes the mistake of assuming that every unmerged file will always have a stage 1 conflict, and this results in conflicts that occur in stage 3 only being dropped. This PR simply adjusts this code to ensure that any empty unmergedFile will always be passed down the channel. The PR also adds a lot of Trace commands to attempt to help find future bugs in this code. Fix #19527 Signed-off-by: Andrew Thornton <[email protected]>
1 parent 99f2f82 commit 7a428fa

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

services/pull/patch.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ func (e *errMergeConflict) Error() string {
124124
}
125125

126126
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error {
127+
log.Trace("Attempt to merge:\n%v", file)
127128
switch {
128129
case file.stage1 != nil && (file.stage2 == nil || file.stage3 == nil):
129130
// 1. Deleted in one or both:
@@ -295,7 +296,8 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
295296
var treeHash string
296297
treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath})
297298
if err != nil {
298-
return false, err
299+
lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath})
300+
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
299301
}
300302
treeHash = strings.TrimSpace(treeHash)
301303
baseTree, err := gitRepo.GetTree("base")

services/pull/patch_unmerged.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ func (line *lsFileLine) SameAs(other *lsFileLine) bool {
4242
line.path == other.path
4343
}
4444

45+
// String provides a string representation for logging
46+
func (line *lsFileLine) String() string {
47+
if line == nil {
48+
return "<nil>"
49+
}
50+
if line.err != nil {
51+
return fmt.Sprintf("%d %s %s %s %v", line.stage, line.mode, line.path, line.sha, line.err)
52+
}
53+
return fmt.Sprintf("%d %s %s %s", line.stage, line.mode, line.path, line.sha)
54+
}
55+
4556
// readUnmergedLsFileLines calls git ls-files -u -z and parses the lines into mode-sha-stage-path quadruplets
4657
// it will push these to the provided channel closing it at the end
4758
func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan chan *lsFileLine) {
@@ -118,6 +129,17 @@ type unmergedFile struct {
118129
err error
119130
}
120131

132+
// String provides a string representation of the an unmerged file for logging
133+
func (u *unmergedFile) String() string {
134+
if u == nil {
135+
return "<nil>"
136+
}
137+
if u.err != nil {
138+
return fmt.Sprintf("error: %v\n%v\n%v\n%v", u.err, u.stage1, u.stage2, u.stage3)
139+
}
140+
return fmt.Sprintf("%v\n%v\n%v", u.stage1, u.stage2, u.stage3)
141+
}
142+
121143
// unmergedFiles will collate the output from readUnstagedLsFileLines in to file triplets and send them
122144
// to the provided channel, closing at the end.
123145
func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmergedFile) {
@@ -138,6 +160,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer
138160

139161
next := &unmergedFile{}
140162
for line := range lsFileLineChan {
163+
log.Trace("Got line: %v Current State:\n%v", line, next)
141164
if line.err != nil {
142165
log.Error("Unable to run ls-files -u -z! Error: %v", line.err)
143166
unmerged <- &unmergedFile{err: fmt.Errorf("unable to run ls-files -u -z! Error: %v", line.err)}
@@ -149,7 +172,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer
149172
case 0:
150173
// Should not happen as this represents successfully merged file - we will tolerate and ignore though
151174
case 1:
152-
if next.stage1 != nil {
175+
if next.stage1 != nil || next.stage2 != nil || next.stage3 != nil {
153176
// We need to handle the unstaged file stage1,stage2,stage3
154177
unmerged <- next
155178
}

0 commit comments

Comments
 (0)