<!doctype html> <html lang="en" class="no-js"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="canonical" href="https://martinvonz.github.io/jj/v0.9.0/technical/architecture.html"> <link rel="prev" href="../related-work.html"> <link rel="next" href="concurrency.html"> <link rel="icon" href="../assets/images/favicon.png"> <meta name="generator" content="mkdocs-1.5.2, mkdocs-material-9.2.5"> <title>Architecture - Jujutsu docs</title> <link rel="stylesheet" href="../assets/stylesheets/main.0e669242.min.css"> <script src="https://unpkg.com/iframe-worker/shim"></script> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"> <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style> <script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script> </head> <body dir="ltr"> <script>var palette=__md_get("__palette");if(palette&&"object"==typeof palette.color)for(var key of Object.keys(palette.color))document.body.setAttribute("data-md-color-"+key,palette.color[key])</script> <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off"> <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off"> <label class="md-overlay" for="__drawer"></label> <div data-md-component="skip"> <a href="#architecture" class="md-skip"> Skip to content </a> </div> <div data-md-component="announce"> </div> <div data-md-color-scheme="default" data-md-component="outdated" hidden> </div> <header class="md-header md-header--shadow" data-md-component="header"> <nav class="md-header__inner md-grid" aria-label="Header"> <a href=".." title="Jujutsu docs" class="md-header__button md-logo" aria-label="Jujutsu docs" data-md-component="logo"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg> </a> <label class="md-header__button md-icon" for="__drawer"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg> </label> <div class="md-header__title" data-md-component="header-title"> <div class="md-header__ellipsis"> <div class="md-header__topic"> <span class="md-ellipsis"> Jujutsu docs </span> </div> <div class="md-header__topic" data-md-component="header-topic"> <span class="md-ellipsis"> Architecture </span> </div> </div> </div> <label class="md-header__button md-icon" for="__search"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> </label> <div class="md-search" data-md-component="search" role="dialog"> <label class="md-search__overlay" for="__search"></label> <div class="md-search__inner" role="search"> <form class="md-search__form" name="search"> <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required> <label class="md-search__icon md-icon" for="__search"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> </label> <nav class="md-search__options" aria-label="Search"> <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg> </button> </nav> </form> <div class="md-search__output"> <div class="md-search__scrollwrap" data-md-scrollfix> <div class="md-search-result" data-md-component="search-result"> <div class="md-search-result__meta"> Initializing search </div> <ol class="md-search-result__list" role="presentation"></ol> </div> </div> </div> </div> </div> </nav> </header> <div class="md-container" data-md-component="container"> <main class="md-main" data-md-component="main"> <div class="md-main__inner md-grid"> <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" > <div class="md-sidebar__scrollwrap"> <div class="md-sidebar__inner"> <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0"> <label class="md-nav__title" for="__drawer"> <a href=".." title="Jujutsu docs" class="md-nav__button md-logo" aria-label="Jujutsu docs" data-md-component="logo"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg> </a> Jujutsu docs </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_1" > <label class="md-nav__link" for="__nav_1" id="__nav_1_label" tabindex="0"> <span class="md-ellipsis"> Getting started </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_1_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_1"> <span class="md-nav__icon md-icon"></span> Getting started </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../install-and-setup.html" class="md-nav__link"> <span class="md-ellipsis"> Installation and Setup </span> </a> </li> <li class="md-nav__item"> <a href="../tutorial.html" class="md-nav__link"> <span class="md-ellipsis"> Tutorial and Birds-Eye View </span> </a> </li> <li class="md-nav__item"> <a href="../github.html" class="md-nav__link"> <span class="md-ellipsis"> Working with GitHub </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../FAQ.html" class="md-nav__link"> <span class="md-ellipsis"> FAQ </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" > <label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0"> <span class="md-ellipsis"> Concepts </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_3"> <span class="md-nav__icon md-icon"></span> Concepts </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../working-copy.html" class="md-nav__link"> <span class="md-ellipsis"> Working Copy </span> </a> </li> <li class="md-nav__item"> <a href="../branches.html" class="md-nav__link"> <span class="md-ellipsis"> Branches </span> </a> </li> <li class="md-nav__item"> <a href="../conflicts.html" class="md-nav__link"> <span class="md-ellipsis"> Conflicts </span> </a> </li> <li class="md-nav__item"> <a href="../operation-log.html" class="md-nav__link"> <span class="md-ellipsis"> Operation Log </span> </a> </li> <li class="md-nav__item"> <a href="../glossary.html" class="md-nav__link"> <span class="md-ellipsis"> Glossary </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" > <label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0"> <span class="md-ellipsis"> Configuration </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4"> <span class="md-nav__icon md-icon"></span> Configuration </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../config.html" class="md-nav__link"> <span class="md-ellipsis"> Settings </span> </a> </li> <li class="md-nav__item"> <a href="../revsets.html" class="md-nav__link"> <span class="md-ellipsis"> Revset language </span> </a> </li> <li class="md-nav__item"> <a href="../templates.html" class="md-nav__link"> <span class="md-ellipsis"> Templating language </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" > <label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0"> <span class="md-ellipsis"> Comparisons </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5"> <span class="md-nav__icon md-icon"></span> Comparisons </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../git-comparison.html" class="md-nav__link"> <span class="md-ellipsis"> Git comparison </span> </a> </li> <li class="md-nav__item"> <a href="../git-compatibility.html" class="md-nav__link"> <span class="md-ellipsis"> Git compatibility </span> </a> </li> <li class="md-nav__item"> <a href="../sapling-comparison.html" class="md-nav__link"> <span class="md-ellipsis"> Sapling </span> </a> </li> <li class="md-nav__item"> <a href="../related-work.html" class="md-nav__link"> <span class="md-ellipsis"> Other related work </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--active md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" checked> <label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0"> <span class="md-ellipsis"> Technical details </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="true"> <label class="md-nav__title" for="__nav_6"> <span class="md-nav__icon md-icon"></span> Technical details </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item md-nav__item--active"> <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc"> <label class="md-nav__link md-nav__link--active" for="__toc"> <span class="md-ellipsis"> Architecture </span> <span class="md-nav__icon md-icon"></span> </label> <a href="architecture.html" class="md-nav__link md-nav__link--active"> <span class="md-ellipsis"> Architecture </span> </a> <nav class="md-nav md-nav--secondary" aria-label="Table of contents"> <label class="md-nav__title" for="__toc"> <span class="md-nav__icon md-icon"></span> Table of contents </label> <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> <li class="md-nav__item"> <a href="#data-model" class="md-nav__link"> Data model </a> </li> <li class="md-nav__item"> <a href="#separation-of-library-from-ui" class="md-nav__link"> Separation of library from UI </a> </li> <li class="md-nav__item"> <a href="#storage-independent-apis" class="md-nav__link"> Storage-independent APIs </a> </li> <li class="md-nav__item"> <a href="#design-of-the-library-crate" class="md-nav__link"> Design of the library crate </a> <nav class="md-nav" aria-label="Design of the library crate"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#overview" class="md-nav__link"> Overview </a> </li> <li class="md-nav__item"> <a href="#backend" class="md-nav__link"> Backend </a> </li> <li class="md-nav__item"> <a href="#gitbackend" class="md-nav__link"> GitBackend </a> </li> <li class="md-nav__item"> <a href="#localbackend" class="md-nav__link"> LocalBackend </a> </li> <li class="md-nav__item"> <a href="#store" class="md-nav__link"> Store </a> </li> <li class="md-nav__item"> <a href="#readonlyrepo" class="md-nav__link"> ReadonlyRepo </a> </li> <li class="md-nav__item"> <a href="#mutablerepo" class="md-nav__link"> MutableRepo </a> </li> <li class="md-nav__item"> <a href="#transaction" class="md-nav__link"> Transaction </a> </li> <li class="md-nav__item"> <a href="#repoloader" class="md-nav__link"> RepoLoader </a> </li> <li class="md-nav__item"> <a href="#treestate" class="md-nav__link"> TreeState </a> </li> <li class="md-nav__item"> <a href="#workingcopy" class="md-nav__link"> WorkingCopy </a> </li> <li class="md-nav__item"> <a href="#workspace" class="md-nav__link"> Workspace </a> </li> <li class="md-nav__item"> <a href="#git" class="md-nav__link"> Git </a> </li> <li class="md-nav__item"> <a href="#revsets" class="md-nav__link"> Revsets </a> </li> <li class="md-nav__item"> <a href="#stackedtable" class="md-nav__link"> StackedTable </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#design-of-the-cli-crate" class="md-nav__link"> Design of the CLI crate </a> <nav class="md-nav" aria-label="Design of the CLI crate"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#templates" class="md-nav__link"> Templates </a> </li> <li class="md-nav__item"> <a href="#diff-editing" class="md-nav__link"> Diff-editing </a> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="concurrency.html" class="md-nav__link"> <span class="md-ellipsis"> Concurrency </span> </a> </li> <li class="md-nav__item"> <a href="conflicts.html" class="md-nav__link"> <span class="md-ellipsis"> Conflicts </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7" > <label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0"> <span class="md-ellipsis"> Contributing </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_7"> <span class="md-nav__icon md-icon"></span> Contributing </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../contributing.html" class="md-nav__link"> <span class="md-ellipsis"> Guidelines and "How to...?" </span> </a> </li> <li class="md-nav__item"> <a href="../code-of-conduct.html" class="md-nav__link"> <span class="md-ellipsis"> Code of conduct </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8" > <label class="md-nav__link" for="__nav_8" id="__nav_8_label" tabindex="0"> <span class="md-ellipsis"> Design docs </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8"> <span class="md-nav__icon md-icon"></span> Design docs </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../design/git-submodules.html" class="md-nav__link"> <span class="md-ellipsis"> git-submodules </span> </a> </li> <li class="md-nav__item"> <a href="../design/git-submodule-storage.html" class="md-nav__link"> <span class="md-ellipsis"> git-submodule-storage </span> </a> </li> <li class="md-nav__item"> <a href="../design/tracking-branches.html" class="md-nav__link"> <span class="md-ellipsis"> Tracking branches </span> </a> </li> </ul> </nav> </li> </ul> </nav> </div> </div> </div> <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" > <div class="md-sidebar__scrollwrap"> <div class="md-sidebar__inner"> <nav class="md-nav md-nav--secondary" aria-label="Table of contents"> <label class="md-nav__title" for="__toc"> <span class="md-nav__icon md-icon"></span> Table of contents </label> <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> <li class="md-nav__item"> <a href="#data-model" class="md-nav__link"> Data model </a> </li> <li class="md-nav__item"> <a href="#separation-of-library-from-ui" class="md-nav__link"> Separation of library from UI </a> </li> <li class="md-nav__item"> <a href="#storage-independent-apis" class="md-nav__link"> Storage-independent APIs </a> </li> <li class="md-nav__item"> <a href="#design-of-the-library-crate" class="md-nav__link"> Design of the library crate </a> <nav class="md-nav" aria-label="Design of the library crate"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#overview" class="md-nav__link"> Overview </a> </li> <li class="md-nav__item"> <a href="#backend" class="md-nav__link"> Backend </a> </li> <li class="md-nav__item"> <a href="#gitbackend" class="md-nav__link"> GitBackend </a> </li> <li class="md-nav__item"> <a href="#localbackend" class="md-nav__link"> LocalBackend </a> </li> <li class="md-nav__item"> <a href="#store" class="md-nav__link"> Store </a> </li> <li class="md-nav__item"> <a href="#readonlyrepo" class="md-nav__link"> ReadonlyRepo </a> </li> <li class="md-nav__item"> <a href="#mutablerepo" class="md-nav__link"> MutableRepo </a> </li> <li class="md-nav__item"> <a href="#transaction" class="md-nav__link"> Transaction </a> </li> <li class="md-nav__item"> <a href="#repoloader" class="md-nav__link"> RepoLoader </a> </li> <li class="md-nav__item"> <a href="#treestate" class="md-nav__link"> TreeState </a> </li> <li class="md-nav__item"> <a href="#workingcopy" class="md-nav__link"> WorkingCopy </a> </li> <li class="md-nav__item"> <a href="#workspace" class="md-nav__link"> Workspace </a> </li> <li class="md-nav__item"> <a href="#git" class="md-nav__link"> Git </a> </li> <li class="md-nav__item"> <a href="#revsets" class="md-nav__link"> Revsets </a> </li> <li class="md-nav__item"> <a href="#stackedtable" class="md-nav__link"> StackedTable </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#design-of-the-cli-crate" class="md-nav__link"> Design of the CLI crate </a> <nav class="md-nav" aria-label="Design of the CLI crate"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#templates" class="md-nav__link"> Templates </a> </li> <li class="md-nav__item"> <a href="#diff-editing" class="md-nav__link"> Diff-editing </a> </li> </ul> </nav> </li> </ul> </nav> </div> </div> </div> <div class="md-content" data-md-component="content"> <article class="md-content__inner md-typeset"> <h1 id="architecture">Architecture<a class="headerlink" href="#architecture" title="Permanent link">¶</a></h1> <h2 id="data-model">Data model<a class="headerlink" href="#data-model" title="Permanent link">¶</a></h2> <p>The commit data model is similar to <a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects">Git's object model</a> , but with some differences.</p> <h2 id="separation-of-library-from-ui">Separation of library from UI<a class="headerlink" href="#separation-of-library-from-ui" title="Permanent link">¶</a></h2> <p>The <code>jj</code> binary consists of two Rust crates: the library crate (<code>jj-lib</code>) and the CLI crate (<code>jj-cli</code>). The library crate is currently only used by the CLI crate, but it is meant to also be usable from a GUI or TUI, or in a server serving requests from multiple users. As a result, the library should avoid interacting directly with the user via the terminal or by other means; all input/output is handled by the CLI crate <sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>. Since the library crate is meant to usable in a server, it also cannot read configuration from the user's home directory, or from user-specific environment variables.</p> <p>A lot of thought has gone into making the library crate's API easy to use, but not much has gone into "details" such as which collection types are used, or which symbols are exposed in the API.</p> <h2 id="storage-independent-apis">Storage-independent APIs<a class="headerlink" href="#storage-independent-apis" title="Permanent link">¶</a></h2> <p>One overarching principle in the design is that it should be easy to change where data is stored. The goal was to be able to put storage on local-disk by default but also be able to move storage to the cloud at Google (and for anyone). To that end, commits (and trees, files, etc.) are stored by the commit backend, operations (and views) are stored by the operation backend, the heads of the operation log are stored by the "op heads" backend, the commit index is stored by the index backend, and the working copy is stored by the working copy backend. The interfaces are defined in terms of plain Rust data types, not tied to a specific format. The working copy doesn't have its own trait defined yet, but its interface is small and easy to create traits for when needed.</p> <p>The commit backend to use when loading a repo is specified in the <code>.jj/repo/store/type</code> file. There are similar files for the other backends (<code>.jj/repo/index/type</code>, <code>.jj/repo/op_store/type</code>, <code>.jj/repo/op_heads/type</code>).</p> <h2 id="design-of-the-library-crate">Design of the library crate<a class="headerlink" href="#design-of-the-library-crate" title="Permanent link">¶</a></h2> <h3 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">¶</a></h3> <p>Here's a diagram showing some important types in the library crate. The following sections describe each component.</p> <div class="highlight"><pre><span></span><code>graph TD; ReadonlyRepo-->Store; ReadonlyRepo-->OpStore; ReadonlyRepo-->OpHeadsStore; ReadonlyRepo-->ReadonlyIndex MutableIndex-->ReadonlyIndex; Store-->Backend; GitBackend-->Backend; LocalBackend-->Backend; LocalBackend-->StackedTable; MutableRepo-->ReadonlyRepo; MutableRepo-->MutableIndex; Transaction-->MutableRepo; WorkingCopy-->TreeState; Workspace-->WorkingCopy; Workspace-->RepoLoader; RepoLoader-->Store; RepoLoader-->OpStore; RepoLoader-->OpHeadsStore; RepoLoader-->ReadonlyRepo; Git-->GitBackend; GitBackend-->StackedTable; </code></pre></div> <h3 id="backend">Backend<a class="headerlink" href="#backend" title="Permanent link">¶</a></h3> <p>The <code>Backend</code> trait defines the interface each commit backend needs to implement. The current in-tree commit backends are <code>GitBackend</code> and <code>LocalBackend</code>.</p> <p>Since there are non-commit backends, the <code>Backend</code> trait should probably be renamed to <code>CommitBackend</code>.</p> <h3 id="gitbackend">GitBackend<a class="headerlink" href="#gitbackend" title="Permanent link">¶</a></h3> <p>The <code>GitBackend</code> stores commits in a Git repository. It uses <code>libgit2</code> to read and write commits and refs.</p> <p>To prevent GC from deleting commits that are still reachable from the operation log, the <code>GitBackend</code> stores a ref for each commit in the operation log in the <code>refs/jj/keep/</code> namespace.</p> <p>Commit data that is available in Jujutsu's model but not in Git's model is stored in a <code>StackedTable</code> in <code>.jj/repo/store/extra/</code>. That is currently the change ID and the list of predecessors. For commits that don't have any data in that table, which is any commit created by <code>git</code>, we use an empty list as predecessors, and the bit-reversed commit ID as change ID.</p> <p>Because we use the Git Object ID as commit ID, two commits that differ only in their change ID, for example, will get the same commit ID, so we error out when trying to write the second one of them.</p> <h3 id="localbackend">LocalBackend<a class="headerlink" href="#localbackend" title="Permanent link">¶</a></h3> <p>The <code>LocalBackend</code> is just a proof of concept. It stores objects addressed by their hash, with one file per object.</p> <h3 id="store">Store<a class="headerlink" href="#store" title="Permanent link">¶</a></h3> <p>The <code>Store</code> type wraps the <code>Backend</code> and returns wrapped types for commits and trees to make them easier to use. The wrapped objects have a reference to the <code>Store</code> itself, so you can do e.g. <code>commit.parents()</code> without having to provide the <code>Store</code> as an argument.</p> <p>The <code>Store</code> type also provides caching of commits and trees.</p> <h3 id="readonlyrepo">ReadonlyRepo<a class="headerlink" href="#readonlyrepo" title="Permanent link">¶</a></h3> <p>A <code>ReadonlyRepo</code> represents the state of a repo at a specific operation. It keeps the view object associated with that operation.</p> <p>The repository doesn't know where on disk any working copies live. It knows, via the view object, which commit is supposed to be the current working-copy commit in each workspace.</p> <h3 id="mutablerepo">MutableRepo<a class="headerlink" href="#mutablerepo" title="Permanent link">¶</a></h3> <p>A <code>MutableRepo</code> is a mutable version of <code>ReadonlyRepo</code>. It has a reference to its base <code>ReadonlyRepo</code>, but it has its own copy of the view object and lets the caller modify it.</p> <h3 id="transaction">Transaction<a class="headerlink" href="#transaction" title="Permanent link">¶</a></h3> <p>The <code>Transaction</code> object has a <code>MutableRepo</code> and metadata that will go into the operation log. When the transaction commits, the <code>MutableRepo</code> becomes a view object in the operation log on disk, and the <code>Transaction</code> object becomes an operation object. In memory, <code>Transaction::commit()</code> returns a new <code>ReadonlyRepo</code>.</p> <h3 id="repoloader">RepoLoader<a class="headerlink" href="#repoloader" title="Permanent link">¶</a></h3> <p>The <code>RepoLoader</code> represents a repository at an unspecified operation. You can think of as a pointer to the <code>.jj/repo/</code> directory. It can create a <code>ReadonlyRepo</code> given an operation ID.</p> <h3 id="treestate">TreeState<a class="headerlink" href="#treestate" title="Permanent link">¶</a></h3> <p>The <code>TreeState</code> type represents the state of the files in a working copy. It keep track of the mtime and size for each tracked file. It knows the <code>TreeId</code> that the working copy represents. It has a <code>snapshot()</code> method that will use the recorded mtimes and sizes and detect changes in the working copy. If anything changed, it will return a new <code>TreeId</code>. It also has <code>checkout()</code> for updating the files on disk to match a requested <code>TreeId</code>.</p> <p>The <code>TreeState</code> type supports sparse checkouts. In fact, all working copies are sparse; they simply track the full repo in most cases.</p> <h3 id="workingcopy">WorkingCopy<a class="headerlink" href="#workingcopy" title="Permanent link">¶</a></h3> <p>The <code>WorkingCopy</code> type has a <code>TreeState</code> but also knows which <code>WorkspaceId</code> it has and at which operation it was most recently updated.</p> <h3 id="workspace">Workspace<a class="headerlink" href="#workspace" title="Permanent link">¶</a></h3> <p>The <code>Workspace</code> type represents the combination of a repo and a working copy ( like Git's 'worktree' concept).</p> <p>The repo view at the current operation determines the desired working-copy commit in each workspace. The <code>WorkingCopy</code> determines what is actually in the working copy. The working copy can become stale if the working-copy commit was changed from another workspace (or if the process updating the working copy crashed, for example).</p> <h3 id="git">Git<a class="headerlink" href="#git" title="Permanent link">¶</a></h3> <p>The <code>git</code> module contains functionality for interoperating with a Git repo, at a higher level than the <code>GitBackend</code>. The <code>GitBackend</code> is restricted by the <code>Backend</code> trait; the <code>git</code> module is specifically for Git-backed repos. It has functionality for importing refs from the Git repo and for exporting to refs in the Git repo. It also has functionality for pushing and pulling to/from Git remotes.</p> <h3 id="revsets">Revsets<a class="headerlink" href="#revsets" title="Permanent link">¶</a></h3> <p>A user-provided revset expression string goes through a few different stages to be evaluated:</p> <ol> <li>Parse the expression into a <code>RevsetExpression</code>, which is close to an AST</li> <li>Resolve symbols and functions like <code>tags()</code> into specific commits. After this stage, the expression is still a <code>RevsetExpression</code>, but it won't have any <code>CommitRef</code> variants in it.</li> <li>Resolve visibility. This stage resolves <code>visible_heads()</code> and <code>all()</code> and produces a <code>ResolvedExpression</code>.</li> <li>Evaluate the <code>ResolvedExpression</code> into a <code>Revset</code>.</li> </ol> <p>This evaluation step is performed by <code>Index::evaluate_revset()</code>, allowing the <code>Revset</code> implementation to leverage the specifics of a custom index implementation. The first three steps are independent of the index implementation.</p> <h3 id="stackedtable">StackedTable<a class="headerlink" href="#stackedtable" title="Permanent link">¶</a></h3> <p><code>StackedTable</code> (actually <code>ReadonlyTable</code> and <code>MutableTable</code>) is a simple disk format for storing key-value pairs sorted by key. The keys have to have the same size but the values can have different sizes. We use our own format because we want <a href="concurrency.html">lock-free concurrency</a> and there doesn't seem to be an existing key-value store we could use.</p> <p>The file format contains a lookup table followed by concatenated values. The lookup table is a sorted list of keys, where each key is followed by the associated value's offset in the concatenated values.</p> <p>A table can have a parent table. When looking up a key, if it's not found in the current table, the parent table is searched. We never update a table in place. If the number of new entries to write is less than half the number of entries in the parent table, we create a new table with the new entries and a pointer to the parent. Otherwise, we copy the entries from the parent table and the new entries into a new table with the grandparent as the parent. We do that recursively so parent tables are at least 2 times as large as child tables. This results in O(log N) amortized insertion time and lookup time.</p> <p>There's no garbage collection of unreachable tables yet.</p> <p>The tables are named by their hash. We keep a separate directory of pointers to the current leaf tables, in the same way as we do <a href="concurrency.html#storage">for the operation log</a>.</p> <h2 id="design-of-the-cli-crate">Design of the CLI crate<a class="headerlink" href="#design-of-the-cli-crate" title="Permanent link">¶</a></h2> <h3 id="templates">Templates<a class="headerlink" href="#templates" title="Permanent link">¶</a></h3> <p>The concept is copied from Mercurial, but the syntax is different. The main difference is that the top-level expression is a template expression, not a string like in Mercurial. There is also no string interpolation (e.g. <code>"Commit ID: {node}"</code> in Mercurial).</p> <h3 id="diff-editing">Diff-editing<a class="headerlink" href="#diff-editing" title="Permanent link">¶</a></h3> <p>Diff-editing works by creating two very sparse working copies, containing only the files we want the user to edit. We then let the user edit the right-hand side of the diff. Then we simply snapshot that working copy to create the new tree.</p> <div class="footnote"> <hr /> <ol> <li id="fn:1"> <p>There are a few exceptions, such as for messages printed during automatic upgrades of the repo format <a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a></p> </li> </ol> </div> </article> </div> </div> </main> <footer class="md-footer"> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> Made with <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> Material for MkDocs </a> </div> </div> </div> </footer> </div> <div class="md-dialog" data-md-component="dialog"> <div class="md-dialog__inner md-typeset"></div> </div> <script id="__config" type="application/json">{"base": "..", "features": [], "search": "../assets/javascripts/workers/search.dfff1995.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"provider": "mike"}}</script> <script src="../assets/javascripts/bundle.78eede0e.min.js"></script> </body> </html>