Artifact 421678c29af1ebd435c8c3fb2f609c2aab486a9b429907d5bfc82d5500f3865a:
- File
CHANGELOG.md
— part of check-in
[75f3521e54]
at
2026-07-19 16:31:18
on branch trunk
— checkout: Update returns UpdateResult so conflicts are visible (#61)
* checkout: Update returns UpdateResult so conflicts are visible
The internal 3-way merge already tracked which files were written, removed, and left with conflict markers, but Checkout.Update discarded all of it and returned a bare error. That collapsed a clean update and one that silently wrote <<<<<<< markers into working-tree files into the same outcome, with no way for a caller to tell them apart.
Update now returns (UpdateResult, error). UpdateResult.Conflicted lists paths that were merged but not cleanly -- this is still a successful update (err == nil), not an error, since the working tree is usable and the caller just needs to know which files to show a user. A genuine failure still returns a non-nil error paired with a zero-value UpdateResult. FilesWritten/FilesRemoved are populated the same way, as paths rather than counts.
Threading the paths up required changing the internal update's processFileUpdates from returning bare int counts to returning the actual []string paths, since the count was all that existed before. Checkout.Extract is untouched -- it has no conflict concept.
Breaking change, clean break at v0.7.0 (pre-1.0).
* checkout: sort Update result paths, document FilesWritten/Conflicted overlap
Update's result slices were built by ranging over maps.allNames, a map[string]bool, so their order was whatever Go's randomized map iteration happened to produce that run. A caller diffing result.FilesWritten against a golden file, or asserting on it with reflect.DeepEqual, would see it fail intermittently for no reason related to their code.
Sort all three slices (sort.Strings) right before Update returns. Added a test that touches four files -- two conflicting, two clean additions, named so map iteration order is very unlikely to hand them back sorted by luck -- and asserts the exact sorted slice on both FilesWritten and Conflicted. Verified it fails reliably (5/5 runs) with the sort calls temporarily removed, then restored the fix.
Also stated outright, in both the internal and public UpdateResult doc comments, what "clean or not" was only implying: every path in Conflicted also appears in FilesWritten, since the conflict markers are what got written there. (user: [email protected], size: 4043) [annotate] [blame] [check-ins using]
# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Changed - **Breaking:** `Checkout.Update` now returns `(UpdateResult, error)` instead of a bare `error`. The internal 3-way merge already tracked which files were written, removed, and left with conflict markers; the old signature discarded all of it, so a caller could not tell a clean update from one that silently wrote `<<<<<<<` conflict markers into working-tree files. `UpdateResult.Conflicted` lists the paths that were merged but not cleanly — this is a successful update (`err == nil`), not an error; a genuine failure still returns a non-nil `error` with a zero-value `UpdateResult`. `Checkout.Extract` is unchanged. - **Breaking:** `Repo.Timeline` now enumerates the repository's `event` table newest-first (every event kind by default, or a single kind via `TimelineOpts.Type`), matching canonical `fossil timeline`. The previous behavior — a first-parent walk from a required start rid — is preserved under a new, honestly-named method, `Repo.Ancestry(LogOpts)`. The old `Repo.Timeline(LogOpts)` was actually an ancestry walk masquerading as an enumeration: it never visited a second parent or a sibling branch head, so it silently omitted any check-in that wasn't a first-parent ancestor of the given start rid. There is no deprecated shim; callers of the old `Timeline` should switch to `Ancestry` if they want the walk, or adopt the new `Timeline(TimelineOpts)` if they want a full enumeration. - `LogEntry` gains a `Kind` field (`EventKind`) identifying which of `event.type`'s six kinds (`ci`, `e`, `f`, `g`, `t`, `w`) an entry is. `Parents` is only populated for `Kind == EventKindCheckin`. - **Breaking:** `Repo.Timeline` orders by `(mtime DESC, rid DESC)`, a total order with rid as a true tie-break at exact mtime equality — a deliberate improvement over canonical fossil's bare `mtime DESC` with no tie-break, which can repeat or skip rows at a page boundary. Pagination uses a new opaque `Cursor` type: take one from a returned `LogEntry.Cursor` and pass it back as `TimelineOpts.After` to resume immediately after that entry. **`TimelineOpts.Before time.Time` and `TimelineOpts.After FslID` are deleted, not deprecated** — callers constructing either field will fail to compile. `Cursor`'s representation is intentionally hidden — it can only be obtained from a `LogEntry`, never built from a timestamp and a rid by hand, because a hand-built cursor derived from a rounded `time.Time` is not guaranteed to match its row exactly, which is what reintroduces skipped or duplicated rows at a page boundary in the first place. ## [0.1.0] - 2026-04-20 Initial open-source release of `libfossil`, a pure-Go implementation of the Fossil SCM that reads and writes the same `.fossil` SQLite repository format. ### Added - Repository lifecycle: create new repos and clone from existing ones. - Working-tree operations: checkout and checkin. - Timeline traversal over commits and events. - Merge and rebase primitives. - Diff and annotate (blame) over tracked content. - Manifest parsing and content-addressed blob storage. - Sync protocol client/server for pulling and pushing between repos. - Observer interfaces for sync and checkout, allowing external hooks into both network sync events and working-tree state transitions. - SQLite driver abstraction with support for both `modernc.org/sqlite` (pure Go) and `ncruces/go-sqlite3` (cgo-free, wasm-based) backends. - Deterministic simulation test harness with BUGGIFY-style fault injection for exercising concurrency and failure paths. - OpenTelemetry observer provided as a separate submodule to keep the core dependency footprint small. - `wasip1/wasm` build target for running `libfossil` under WASI runtimes.