Artifact 4ff704cc4be8c52ed408b6aa6a129f02fd96f3338d5ccff6720c6a6c3e7ae445:
- File
cli/repo_merge_resolve.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: 1583) [annotate] [blame] [check-ins using]
package cli import ( "fmt" "slices" ) // RepoMergeResolveCmd marks a file conflict as resolved. // // A merge conflict needs no marking: as in fossil, a file is in conflict // while it holds conflict markers, and editing them out resolves it. So this // command refuses a file that still holds markers, and resolves the // conflict-fork entries the conflict-fork strategy records. type RepoMergeResolveCmd struct { File string `arg:"" help:"File to mark as resolved"` Dir string `short:"d" help:"Checkout directory" default:"."` } func (c *RepoMergeResolveCmd) Run(g *Globals) (err error) { if _, err := checkoutDBPath(c.Dir); err == nil { conflicted, err := checkoutConflicts(g, c.Dir) if err != nil { return err } if slices.Contains(conflicted, c.File) { return fmt.Errorf("%s still has conflict markers; edit them out to resolve it", c.File) } } r, err := g.OpenRepo() if err != nil { return err } defer closeWith(r.Close, &err) forks, err := r.ListConflictForks() if err != nil { return err } if !slices.Contains(forks, c.File) { return fmt.Errorf("%s: no conflict found", c.File) } if err := r.ResolveConflictFork(c.File); err != nil { return err } fmt.Printf("resolved: %s (conflict-fork)\n", c.File) return nil } // checkoutConflicts lists the files in the checkout in dir that hold // conflict markers. func checkoutConflicts(g *Globals, dir string) (conflicted []string, err error) { _, co, done, err := openWorkingCheckout(g, dir) if err != nil { return nil, err } defer closeWith(done, &err) return co.Conflicts() }