Artifact 4f967b7970b193c0484284a0ff570d92f4f21b11264b236c637a3e96be312794:
- File
internal/merge/marker.go
— part of check-in
[7ea6fba63f]
at
2026-10-06 15:57:25
on branch trunk
— fix(checkout): merge the way fossil does, and commit merges with their parent (#244) (#252)
The CLI's merge marked a conflict with vfile.chnged=5, which fossil reads as "added by integrate", and recorded no merge-in version or vmerge row, so a merge went unseen and the next commit dropped the merge parent.
Merging now lives in the library as Checkout.Merge and records what fossil records: chnged=2 and mrid for a file taken from the merged-in version, chnged=3 for an added file, a per-file vmerge row for a merged file, and vmerge id 0 for the merge. Conflicts use fossil's markers and are found by looking for them (Checkout.Conflicts), so fossil and go-libfossil read each other's merges. Where fossil relies on its undo copy, Merge keeps the file or refuses instead.
Commit reads vmerge for merge parents, and, as fossil's commit does, refuses unresolved conflict markers, empty commits, unknown names and partial commits of a merge. The CLI's merge, conflicts, mark-resolved and ci commands call the library; ci now commits the checkout. (user: [email protected], size: 1971) [annotate] [blame] [check-ins using]
package merge import "bytes" // Conflict markers, byte for byte the lines fossil's merge writes (merge3.c), // so a conflict go-libfossil writes is one fossil recognizes, and the other // way round. Each marker is followed by a newline in the merged output. const ( markerBegin = "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<" markerAncestor = "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||" markerMergedIn = "======= MERGED IN content follows ===============================" markerEnd = ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" // markerSuggested is a section fossil can write between the local copy // and the ancestor. go-libfossil never writes it, but a file fossil wrote // may contain it. markerSuggested = "####### SUGGESTED CONFLICT RESOLUTION follows ###################" ) // markerLen is the length every marker shares; detection compares this many // bytes at the start of each line, as fossil does. const markerLen = len(markerBegin) // Fossil's detector assumes every marker has the same length. var _ = [1]struct{}{}[len(markerAncestor)-markerLen] var _ = [1]struct{}{}[len(markerMergedIn)-markerLen] var _ = [1]struct{}{}[len(markerEnd)-markerLen] var _ = [1]struct{}{}[len(markerSuggested)-markerLen] // HasConflictMarkers reports whether data contains a line that starts with // one of the opening or middle conflict markers. This is fossil's // contains_merge_marker: a file is in conflict exactly when it holds one, so // a conflict is resolved by editing the markers out, with nothing to record. func HasConflictMarkers(data []byte) bool { markers := [...]string{markerBegin, markerSuggested, markerAncestor, markerMergedIn} for len(data) > 0 { for _, m := range markers { if bytes.HasPrefix(data, []byte(m)) { return true } } next := bytes.IndexByte(data, '\n') if next < 0 { return false } data = data[next+1:] } return false }