Repository navigation
The latest release of Lore, Epic Games' next-generation open source version control system.
Quickstart | Design | Documentation | Discord
Quick Install
Windows
irm https://raw.githubusercontent.com/EpicGames/lore/main/scripts/install.ps1 | iex
macOS / Linux
curl -fsSL https://raw.githubusercontent.com/EpicGames/lore/main/scripts/install.sh | bash
Release Notes
Changes since v0.10.0.
Breaking changes
lore.thin_client.v1:DiffChange.content_from/content_toandContentDiffRequest.address_from/address_to/address_basebecome alore.model.v1.Address, the formTreeNode.addressalready carried, and move to field numbers 10 and 11, and 8, 9 and 10; the hash-onlybytesfields they replace (5 and 6, and 1 through 3) are reserved. A committed file's content resolves under a context generated per file, so a consumer keying content and metadata lookups onhash-contextfound nothing for any file in a diff, and had nowhere to put the context when askingContentDifffor the file's text. A side that does not exist reports the field unset: an ADD has no from side and a DELETE no to side. A link pin change addresses each of its revisions under the linked repository. A consumer built against the old fields reads the new ones as absent rather than as a hash, so rebuild against the new bindings- C API: every storage
*_ITEM_COMPLETEevent replaceserror_codewith anerrordetail carrying the failure's own FFI code, message and trace, asCompletealready did, and the call'sstatusbecomes the dominant item failure's code. Re-check any branch on a per-item code: a missing payload reportsPayloadNotFound(81) rather thanAddressNotFound(80), a buffer short of the content reportsOversized(118) rather thanInvalidArguments(3), and most failures previously reported asInternalnow report their own code. The structs grow and are no longer trivially copyable, so copy the detail's strings before the callback returns and rebuild against the newlore.h. The revision-tree per-item events are unchanged: they still carryerror_codeas alore_error_code_t, so the five-value folding still applies to them, and moving them to a detail will need a second rebuild in a later release - C API: an empty
lore_string_tthe library emits now carries a NULLstringpointer rather than a pointer to a zero-length NUL-terminated buffer, which is whatlore_string_thas always documented and what an emptylore_array_talready answers. A consumer that readstringwithout first checkinglength—strlen(s.string),printf("%s", s.string)— must checklength, or treat NULL as the empty string. This applies to every empty string on every event and every verb, not one field - C API:
lore_revision_sync_args_tappendsview, a path to a view filter file, empty to keep the view the instance holds. Rebuild against the newlore.h - Build: the C library is built by the new
lore-capipackage.cargo build -p lorebuilds only the Rust library; build-p lore-capiforliblore.soandliblore.a(liblore.dylib, orlore.dllandlore.libon Windows) andlore.h, under the same names and paths as before. Building the CLI no longer compiles and links the C library - Build: the build version is no longer compiled in from
LORE_BUILD_VERSION_NAME, and a build no longer runslore revision infoto name itself. Every build reports<package version>+localuntil the newlore-stamptool writes a build name into the finished binaries and libraries, which then report<package version>+<name>(seeCONTRIBUTING.md). A pipeline that setsLORE_BUILD_VERSION_NAMEmust runlore-stamp --build <name>after the build and before signing instead. A changing build number no longer invalidates the build cache for every crate lore-server: a[server.auth]without[environment.endpoint] auth_url, which authorizes from token claims, refuses to start without[server.auth.oidc], since clients have no other way to log in. Add the table with the provider's publicclient_id. Tier 2 (resource_claimset) also requiresresource_templateorscope_templatein it, and Tier 1 refuses both
Features
- C API:
lore_revision_cherry_pickandlore_revision_cherry_pick_asynccherry-pick a revision onto the current branch, aslore revision cherry-pickdoes - C API:
lore_branch_latest_listandlore_branch_latest_list_asynclist the revisions a branch LATEST has held, most recent first, and relay to the Lore service when one is in use.lore branch latest listtakes the same path, so with the service in use it no longer waits indefinitely on the local store the service holds lore sync --view <file>changes the view filter an instance materializes its working files under, carrying the working tree to what the new view holds rather than re-cloninglore-server: the disk space available to the local stores is checked on a timer and a warning is logged once it falls below a threshold.[server.local_store_monitor]carriescheck_interval_seconds(default 30) andlow_space_threshold_bytes(default 10 GiB); an interval of 0 turns the check off. Every local store the server writes at is watched, the immutable store and the mutable store among them, and the reading is taken per volume: stores sharing a filesystem draw one warning naming them all, rather than one warning each. A server whose stores are not local is not checkedlore file reset --last-merged-from <branch>takes--mineor--theirs, restoring the version the reset branch held going into that merge or the version the named branch brought in, rather than the version the merge's conflicts were resolved with. A merge revision holds the resolved content, so the side that lost a resolution was otherwise unreachable; where no merge from the branch is found the branch point is restored as before, whichever flag is given. C API:lore_file_reset_to_last_merged_args_tappends amerge_side(0= resolved,1= self,2= other) in the struct's tail padding, so its size and its other fields' offsets are unchanged and a zero-initialized struct keeps today's behavior[environment.endpoint] user_urladvertises a user directory that's decoupled from the auth service. Clients resolve user IDs to display names using this service'sGetUserInfoandGetUserIdoperations. For backward compatibility,auth_urlis used as the user service URL, ifuser_urlis not defined. Client-side, the lookup moves off theAuthenticationtrait onto aUserServicetraitlore-server:lore repository listanswers through aRepositoryCatalogimplementation. The implementation is selected based on server configuration. AUrcAuthApideployment asksLookupUserPermissionsas before, a server with no[server.auth]lists everything it holds. A deployment authorizing from token claims uses a catalog implementation basedrepository_catalogandrepository_catalog_urlconfiguration.- C API:
lore_repository_deleteandlore_repository_delete_asyncdelete a remote repository, and are carried out by the Lore service when one is in use.lore repository deleteis relayed the same way, where it previously always ran in the calling process lore-proto: introduceslore.user.v1.UserService, the user directory in Lore's own terms:UserGetresolves user IDs to users,UserFindresolves a name to a user, andPartitionListstreams the partitions the caller is allowed to see. These take over whatGetUserInfo,GetUserIdandLookupUserPermissionsdid inUrcAuthApi. Only the pure OIDC-compliant authn/authz operations are left inUrcAuthApiso that we can use OIDC authorization as a drop-in replacement for the Auth API.lore-server: the new[server.auth.oidc]table configures OIDC info shown to clients, through the newoidcfield oflore.environment.v1.Environmentlore-server:[server.auth] jwt_typlists the accepted JWTtypheader values, as a string or an array. When set, a token whose header carries notyp, or atypthat doesn't match any of the entries, is rejected. If unset, the tokentypvalue is not checked, as before- C API:
lore_revision_bisectandlore_revision_bisect_asynctake one step of a bisect between two revisions, synchronizing the working directory to the revision halfway between them, and relay to the Lore service when one is in use.lore revision bisecttakes the same path, so with the service in use it no longer waits indefinitely on the local store the service holds - C API:
lore_shared_store_listandlore_shared_store_list_asynclist the registered shared stores and, when asked, the instances using each, aslore shared-store listdoes
Fixes & Improvements
lore link list --staged, and an interactivelore commitlisting the links it asks a message for, run in the Lore service when one is in use, as other commands do, rather than opening the repository in the client, where they waited on the lock of a service holding it. C API:lore_link_list_stagedandlore_link_list_staged_asyncexpose the listinglore commitstops with the failure of listing the links or layers its per-link and per-layer messages are resolved against, such as a Lore service in use that cannot be reached, where it rejected a--link-messageor--layer-messagepath as matching nothing, or committed without asking for those messageslore repository instance pruneno longer removes an SWFS instance when run without the Lore service, where the instance is not mounted and its path is absent or an empty directory. Its external.lorein the global data directory shows it still exists, solore repository instance listreports it as live and prune keeps its branch and revision anchors- Fix resolving another user's name failing against an auth service reached by IP address over
http://orhttps:// - A
lore_string_torlore_binary_ta caller passes in with a NULL pointer and a non-zerolengthreads as empty rather than as undefined behaviour, which is what an emptylore_array_talready did. The library answers NULL for every empty string it emits, so one can come back in an argument struct beside a length the caller kept itself, and the pointer alone decides whether there are bytes to read lore-server: every local store the configuration asks for is reported at startup, composite tiers included. A store the configuration names no path for is reported as unconfigured, wherever the generated fallback points, and a store inside a system temporary directory is reported as ephemeral whether it was configured there or generated there; the two are independent, since either can hold without the other. A local immutable tier naming a path other than the first local tier's is reported as unused instead, every local tier being handed the store the first one creates. The ephemeral report previously fired on a path being absent rather than on where the resolved path pointed, so the shippedlocal.toml, which sets/tmp/lore-serverexplicitly, drew nothinglore-server: logs default towarnrather thanerror, so a server started withoutRUST_LOGreports the conditions an operator has to act on.RUST_LOGstill wins where it is set, andRUST_LOG=errorrestores the previous output- Fix a moved file being deleted when the change that moves it is realized over a working tree that already holds it, which is what re-running an interrupted
lore syncdoes. The rename finds nothing at the source it was already carried from, and the recovery that follows removed the destination and then skipped rewriting it, because whether the content was in place was inferred from the view filter rather than read from the rename. It is now read from the rename, so a move whose source is not there is written from the store, and one the rename carried is left alone - Bound how many subtree tasks the walk between two revisions runs at once, which
lore sync,lore statusandlore diffall drive. It previously ran one task per directory it descends into, all of them live at the same time, so peak memory followed the size of the tree rather than the work in flight; a subtree that finds no room in the budget is now queued for the task that found it to walk, so neither the tasks a walk holds nor the stack it stands on grows with the tree - Fix a command carried out by the service being answered without part of its output. A relayed call returned its result while its events were still being delivered to the caller's callback, so
lore statuscould report a repository with no staged changes. The events are now delivered before the call returns - A call relayed to a Lore service that cannot be reached now reports its status and error detail on the
Completeevent, as a failing command does, rather than on a legacyErrorevent, so a caller of an_asyncentry point receives it - Fix a relative
--repositoryresolving against the Lore service's working directory rather than the caller's for a command carried out by the service.lore repository info,lore repository delete,lore auth info,lore auth loginandlore auth logoutreported no repository or no remote where the path named one. The service now resolves the path against the directory the command was run in - Ignore and view filter files are read as UTF-8, and as UTF-16 of either byte order with a byte-order mark or, where the rules are mostly ASCII, without one. UTF-32, truncated UTF-16, and mark-less UTF-16 that cannot be detected are refused rather than read as UTF-8 into rules carrying NULs, so UTF-16 whose rules lie outside ASCII requires a mark
- Fix the wrong files being removed when a directory leaves the working tree. Where the directory's node could not be read, the removal was decided from the incoming revision's tree and filter rather than the ones the working tree was materialized under, so it could name files that were never on disk and leave behind files that were
- A change to a file's executable bit alone is now reported by
lore status --scan, taken bylore stage, and recorded on the revisionlore commitproduces. A chmod moves neither the content, the size nor the modification time, which were all the comparison read, so the change was invisible from the moment it was made lore syncandlore branch mergekeep a locally changed executable bit on a file whose content they carry, rather than silently reverting it to the revision's. The bit stands as a local modification and blocks nothing;--resetand--forceapply the revision's bit as before- Fix
lore stageover a large tree failing withgrab_node_unused returned INVALID on a freshly-allocated block. A freshly allocated block of node slots was published for every other thread to grab from before the thread that allocated it took its own, so under enough concurrency the block could be emptied out from under it. It takes its slot before it publishes the block lore sync <revision>advances the branch latest to a revision standing ahead of the one the branch holds, the remote answering for it. It was left where it stood, so a sync to the remote's tip drew alore statusreporting the branch behind the remote and a second sync that did not see it was already there. A named revision moves the latest forward only,--localand--dry-runmove it not at all, and a divergent branch keeps the one it has- Fix a moved file being left behind at the path it came from where the move crosses a mount boundary inside the working tree. No rename carries a file between filesystems, and the recovery read a destination that was not there yet as a failure of its own, so the file was written at its new path from the store and the old one never removed. A move no rename can carry now copies the content and removes the source, and a directory is carried a child at a time
- Fix a branch that replaced a committed folder with a link being invisible to a merge, which silently deleted the link and dropped its content. A directory paired with a link is the type change it is:
lore branch diffreports the replacement, and a merge that changed anything below the mount conflicts at the mount - Changes to configuring the Lore service: The service executable field can be left unset if the service was started with lore service run separately. There is no longer a fallback that attempts to use the current binary as the service binary.
lore-server: permission checks are refused withPERMISSION_DENIEDwhen access is checked against the grpc auth service, rather thanINTERNAL- Fix
lore pushslowing quadratically with the number of entries in a changed directory. The push and its server-side verification pair entries in a single pass over name-sorted lists - Fix
lore stagestaging a delete for a removed file or directory that was never committed, whichlore statusreported asDand, afterlore unstage, as an untrackedA.lore syncno longer refuses to run afterlore file dirtydrops such a file
Release Assets
Important
These can be downloaded under the "Assets" section below.
Installer
lore-setup- Lore CLI Windows installer
Binaries
lore- Lore CLIloreserver- Lore Serverliblore- Lore Shared Library
Headers
lore.h- Single header forliblore(packged withliblore)
Supported Platforms
- Windows x84_64
- macOS AArch64
- Linux x86_64
- Linux AArch64 (for Graviton/Neoverse 512-tvb only)
Lore Desktop Client
Important
Lore Desktop Client is available as binaries only, download the installer for your platform here:
- Windows: Lore-Desktop-Setup-x64.exe
- macOS: Lore-Desktop-arm64.dmg
- Linux (Ubuntu 24.04): lore-desktop_amd64.deb
- Linux (AppImage): Lore-Desktop-x86_64.AppImage