ok/jj
1
0
Fork 0
forked from mirrors/jj
jj/v0.17.0/design/sparse-v2/index.html

1853 lines
No EOL
51 KiB
HTML

<!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.17.0/design/sparse-v2/">
<link rel="prev" href="../run/">
<link rel="next" href="../tracking-branches/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.4">
<title>Sparse Patterns v2 - Jujutsu docs</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.50c56a3b.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.06af60db.min.css">
<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" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo">
<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="#sparse-patterns-v2-redesign" 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">
Sparse Patterns v2
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to system preference" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to system preference" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m14.3 16-.7-2h-3.2l-.7 2H7.8L11 7h2l3.2 9h-1.9M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69m-9.15 3.96h2.3L12 9l-1.15 3.65Z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_2" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_2">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12c0-2.42-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69Z"/></svg>
</label>
</form>
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<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">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2" id="__nav_2_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_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<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/" class="md-nav__link">
<span class="md-ellipsis">
Installation and Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../tutorial/" class="md-nav__link">
<span class="md-ellipsis">
Tutorial and Birds-Eye View
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../github/" class="md-nav__link">
<span class="md-ellipsis">
Working with GitHub
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../windows/" class="md-nav__link">
<span class="md-ellipsis">
Working on Windows
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../FAQ/" class="md-nav__link">
<span class="md-ellipsis">
FAQ
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../cli-reference/" class="md-nav__link">
<span class="md-ellipsis">
CLI Reference
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../testimonials/" class="md-nav__link">
<span class="md-ellipsis">
Testimonials
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
<label class="md-nav__link" for="__nav_6" id="__nav_6_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_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<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/" class="md-nav__link">
<span class="md-ellipsis">
Working Copy
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../branches/" class="md-nav__link">
<span class="md-ellipsis">
Branches
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../conflicts/" class="md-nav__link">
<span class="md-ellipsis">
Conflicts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../operation-log/" class="md-nav__link">
<span class="md-ellipsis">
Operation Log
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../glossary/" 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_7" >
<label class="md-nav__link" for="__nav_7" id="__nav_7_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_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7">
<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/" class="md-nav__link">
<span class="md-ellipsis">
Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../filesets/" class="md-nav__link">
<span class="md-ellipsis">
Fileset language
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../revsets/" class="md-nav__link">
<span class="md-ellipsis">
Revset language
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../templates/" 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_8" >
<label class="md-nav__link" for="__nav_8" id="__nav_8_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_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_8">
<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/" class="md-nav__link">
<span class="md-ellipsis">
Git comparison
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../git-compatibility/" class="md-nav__link">
<span class="md-ellipsis">
Git compatibility
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../sapling-comparison/" class="md-nav__link">
<span class="md-ellipsis">
Sapling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../related-work/" 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--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_9" >
<label class="md-nav__link" for="__nav_9" id="__nav_9_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_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_9">
<span class="md-nav__icon md-icon"></span>
Technical details
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../technical/architecture/" class="md-nav__link">
<span class="md-ellipsis">
Architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../technical/concurrency/" class="md-nav__link">
<span class="md-ellipsis">
Concurrency
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../technical/conflicts/" 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_10" >
<label class="md-nav__link" for="__nav_10" id="__nav_10_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_10_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_10">
<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/" 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/" 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--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_11" checked>
<label class="md-nav__link" for="__nav_11" id="__nav_11_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_11_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_11">
<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="../git-submodules/" class="md-nav__link">
<span class="md-ellipsis">
git-submodules
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../git-submodule-storage/" class="md-nav__link">
<span class="md-ellipsis">
git-submodule-storage
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../run/" class="md-nav__link">
<span class="md-ellipsis">
JJ run
</span>
</a>
</li>
<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">
Sparse Patterns v2
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Sparse Patterns v2
</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="#objective" class="md-nav__link">
<span class="md-ellipsis">
Objective
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#current-state-as-of-jj-0130" class="md-nav__link">
<span class="md-ellipsis">
Current State (as of jj 0.13.0)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#proposed-state-sparse-patterns-v2" class="md-nav__link">
<span class="md-ellipsis">
Proposed State (Sparse Patterns v2)
</span>
</a>
<nav class="md-nav" aria-label="Proposed State (Sparse Patterns v2)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-syntax" class="md-nav__link">
<span class="md-ellipsis">
Command Syntax
</span>
</a>
<nav class="md-nav" aria-label="Command Syntax">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#canonicalization" class="md-nav__link">
<span class="md-ellipsis">
Canonicalization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#working-copy-map" class="md-nav__link">
<span class="md-ellipsis">
Working Copy Map
</span>
</a>
<nav class="md-nav" aria-label="Working Copy Map">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-syntax_1" class="md-nav__link">
<span class="md-ellipsis">
Command Syntax
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#versioning-and-storage" class="md-nav__link">
<span class="md-ellipsis">
Versioning and Storage
</span>
</a>
<nav class="md-nav" aria-label="Versioning and Storage">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#view-updates" class="md-nav__link">
<span class="md-ellipsis">
View Updates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#appendix" class="md-nav__link">
<span class="md-ellipsis">
Appendix
</span>
</a>
<nav class="md-nav" aria-label="Appendix">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#related-work" class="md-nav__link">
<span class="md-ellipsis">
Related Work
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#patterns-via-configuration" class="md-nav__link">
<span class="md-ellipsis">
Patterns via configuration
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../tracking-branches/" 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="#objective" class="md-nav__link">
<span class="md-ellipsis">
Objective
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#current-state-as-of-jj-0130" class="md-nav__link">
<span class="md-ellipsis">
Current State (as of jj 0.13.0)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#proposed-state-sparse-patterns-v2" class="md-nav__link">
<span class="md-ellipsis">
Proposed State (Sparse Patterns v2)
</span>
</a>
<nav class="md-nav" aria-label="Proposed State (Sparse Patterns v2)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-syntax" class="md-nav__link">
<span class="md-ellipsis">
Command Syntax
</span>
</a>
<nav class="md-nav" aria-label="Command Syntax">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#canonicalization" class="md-nav__link">
<span class="md-ellipsis">
Canonicalization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#working-copy-map" class="md-nav__link">
<span class="md-ellipsis">
Working Copy Map
</span>
</a>
<nav class="md-nav" aria-label="Working Copy Map">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#command-syntax_1" class="md-nav__link">
<span class="md-ellipsis">
Command Syntax
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#versioning-and-storage" class="md-nav__link">
<span class="md-ellipsis">
Versioning and Storage
</span>
</a>
<nav class="md-nav" aria-label="Versioning and Storage">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#view-updates" class="md-nav__link">
<span class="md-ellipsis">
View Updates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#appendix" class="md-nav__link">
<span class="md-ellipsis">
Appendix
</span>
</a>
<nav class="md-nav" aria-label="Appendix">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#related-work" class="md-nav__link">
<span class="md-ellipsis">
Related Work
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#patterns-via-configuration" class="md-nav__link">
<span class="md-ellipsis">
Patterns via configuration
</span>
</a>
</li>
</ul>
</nav>
</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="sparse-patterns-v2-redesign">Sparse Patterns v2 redesign<a class="headerlink" href="#sparse-patterns-v2-redesign" title="Permanent link">&para;</a></h1>
<p>Authors: <a href="mailto:dploch@google.com">Daniel Ploch</a></p>
<p><strong>Summary:</strong> This Document documents a redesign of the sparse command and
it's internal storage format in jj, in order to facilitate several desirable
improvements for large repos. It covers both the migration path and the planned
end state.</p>
<h2 id="objective">Objective<a class="headerlink" href="#objective" title="Permanent link">&para;</a></h2>
<p>Redesign Sparse Patterns to accommodate more advanced features for native
and custom implementations. This includes three main goals:</p>
<ol>
<li>Sparse Patterns should be versioned with the working copy</li>
<li>Sparse Patterns should support more <a href="https://github.com/martinvonz/jj/issues/1896">flexible matching rules</a></li>
<li>Sparse Patterns should support <a href="https://github.com/martinvonz/jj/issues/2288">client path remapping</a></li>
</ol>
<h2 id="current-state-as-of-jj-0130">Current State (as of jj 0.13.0)<a class="headerlink" href="#current-state-as-of-jj-0130" title="Permanent link">&para;</a></h2>
<p>Sparse patterns are an effectively unordered list of prefix strings:</p>
<div class="highlight"><pre><span></span><code>path/one
path/to/dir/two
</code></pre></div>
<p>The <em>set</em> of files identified by the Sparse Patterns is all paths which match
any provided prefix. This governs what gets materialized in the working copy on
checkout, and what is updated on snapshot. The set is stored in working copy
state files which are not versioned in the Op Store.</p>
<p>Because all paths are bare strings with no escaping or higher-level formatting,
the current design makes it difficult to add new features like exclusions or
path remappings.</p>
<h2 id="proposed-state-sparse-patterns-v2">Proposed State (Sparse Patterns v2)<a class="headerlink" href="#proposed-state-sparse-patterns-v2" title="Permanent link">&para;</a></h2>
<p>Sparse Patterns v2 will be stored as objects in the Op Store, referenced
by a <code>WorkingCopyPatternsId</code> from the active <code>View</code>. They will have a new,
ordered structure which can fully represent previous patterns.</p>
<div class="highlight"><pre><span></span><code><span class="sd">/// Analogues of RepoPath, specifically describing paths in the working copy.</span>
<span class="k">struct</span> <span class="nc">WorkingCopyPathBuf</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nb">String</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="nc">WorkingCopyPath</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">str</span>
<span class="p">}</span>
<span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">SparsePatternsPathType</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Dir</span><span class="p">,</span><span class="w"> </span><span class="c1">// Everything under &lt;path&gt;/...</span>
<span class="w"> </span><span class="n">Files</span><span class="p">,</span><span class="w"> </span><span class="c1">// Files under &lt;path&gt;/*</span>
<span class="w"> </span><span class="n">Exact</span><span class="p">,</span><span class="w"> </span><span class="c1">// &lt;path&gt; exactly</span>
<span class="p">}</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">SparsePatternsPath</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">path_type</span>: <span class="nc">SparsePatternsPathType</span><span class="p">,</span>
<span class="w"> </span><span class="n">include</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="c1">// True if included, false if excluded.</span>
<span class="w"> </span><span class="n">path</span>: <span class="nc">RepoPathBuf</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">WorkingCopyMapping</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">src_path</span>: <span class="nc">RepoPathBuf</span><span class="p">,</span>
<span class="w"> </span><span class="n">dst_path</span>: <span class="nc">WorkingCopyPathBuf</span><span class="p">,</span>
<span class="w"> </span><span class="n">recursive</span>: <span class="kt">bool</span><span class="p">,</span><span class="w"> </span><span class="c1">// If false, only immediate children of src_path (files) are renamed.</span>
<span class="p">}</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">WorkingCopyPatterns</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">sparse_paths</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">SparsePatternsPath</span><span class="o">&gt;</span><span class="p">,</span>
<span class="w"> </span><span class="n">mappings</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">WorkingCopyMapping</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="n">OpStore</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">read_working_copy_patterns</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">id</span>: <span class="kp">&amp;</span><span class="nc">WorkingCopyPatternsId</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">OpStoreResult</span><span class="o">&lt;</span><span class="n">WorkingCopyPatterns</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="p">.</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">write_working_copy_patterns</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">sparse_patterns</span>: <span class="kp">&amp;</span><span class="nc">WorkingCopyPatterns</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">OpStoreResult</span><span class="o">&lt;</span><span class="n">WorkingCopyPatternsId</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>To support these more complex behaviors, a new <code>WorkingCopyPatterns</code> trait will
be introduced, initially only as a thin wrapper around the existing prefix
format, but soon to be expanded with richer types and functionality.</p>
<div class="highlight"><pre><span></span><code><span class="k">impl</span><span class="w"> </span><span class="n">WorkingCopyPatterns</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">to_matcher</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span><span class="w"> </span><span class="n">Matcher</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="o">..</span><span class="p">.</span>
<span class="p">}</span>
</code></pre></div>
<h3 id="command-syntax">Command Syntax<a class="headerlink" href="#command-syntax" title="Permanent link">&para;</a></h3>
<p><code>SparsePatternsPath</code> rules can be specified on the CLI and in an editor via a
compact syntax:</p>
<div class="highlight"><pre><span></span><code>(include|exclude):(dir|files|exact):&lt;path&gt;
</code></pre></div>
<p>If both prefix terms are omitted, then <code>include:dir:</code> is assumed. If any prefix
is specified, both must be specified. The editor and CLI will both accept path
rules in either format going forward.</p>
<ul>
<li><code>jj sparse set --add foo/bar</code> is equal to <code>jj sparse set --add include:dir:foo/bar</code></li>
<li><code>jj sparse set --add exclude:dir:foo/bar</code> adds a new <code>Dir</code> type rule with <code>include = false</code></li>
<li><code>jj sparse set --exclude foo/bar</code> as a possible shorthand for the above</li>
<li><code>jj sparse list</code> will print the explicit rules</li>
</ul>
<p>Paths will be stored in an ordered, canonical form which unambiguously describes
the set of files to be included. Every <code>--add</code> command will append to the end of
this list before the patterns are canonicalized. Whether a file is included is
determined by the first matching rule in reverse order.</p>
<p>For example:</p>
<div class="highlight"><pre><span></span><code>include:dir:foo
exclude:dir:foo/bar
include:dir:foo/bar/baz
exclude:dir:foo/bar/baz/qux
</code></pre></div>
<p>Produces rule set which includes "foo/file.txt", excludes "foo/bar/file.txt",
includes "foo/bar/baz/file.txt", and excludes "foo/bar/baz/qux/file.txt".</p>
<p>If the rules are subtly re-ordered, they become canonicalized to a smaller, but
functionally equivalent form:</p>
<div class="highlight"><pre><span></span><code># Before
include:dir:foo
exclude:dir:foo/bar/baz/qux
include:dir:foo/bar/baz
exclude:dir:foo/bar
# Canonicalized
include:dir:foo
exclude:dir:foo/bar
</code></pre></div>
<h4 id="canonicalization">Canonicalization<a class="headerlink" href="#canonicalization" title="Permanent link">&para;</a></h4>
<p>There are many ways to represent functionally equivalent <code>WorkingCopyPatterns</code>.
For instance, the following 4 rule sets are all functionally equivalent:</p>
<div class="highlight"><pre><span></span><code># Set 1
include:dir:bar
include:dir:foo
# Set 2
include:dir:foo
include:dir:bar
# Set 3
include:dir:bar
include:dir:bar/baz/qux
include:dir:foo
# Set 4
include:dir:foo
exclude:dir:foo/baz
include:dir:bar
include:dir:foo/baz
</code></pre></div>
<p>Because these patterns are stored in the Op Store now, it is useful for all of
these representations to be rewritten into a minimal, canonical form before
serialization. In this case, <code>Set 1</code> will be the canonical set. The canonical
form of a <code>WorkingCopyPatterns</code> is defined as the form such that:</p>
<ul>
<li>Every rule affects the functionality (there are no redundant rules)</li>
<li>Rules are sorted lexicographically, but with '/' sorted before all else<ul>
<li>This special sorting order is useful for constructing path tries</li>
</ul>
</li>
</ul>
<h3 id="working-copy-map">Working Copy Map<a class="headerlink" href="#working-copy-map" title="Permanent link">&para;</a></h3>
<p>WARNING: This section is intentionally lacking, more research is needed.</p>
<p>All <code>WorkingCopyPatterns</code> will come equipped with a default no-op mapping.
These mappings are inspired by and similar to <a href="https://www.perforce.com/manuals/cmdref/Content/CmdRef/views.html">Perforce client views</a>.</p>
<div class="highlight"><pre><span></span><code><span class="fm">vec!</span><span class="p">[</span><span class="n">WorkingCopyMapping</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">src_path</span>: <span class="nc">RepoPathBuf</span>::<span class="n">root</span><span class="p">(),</span>
<span class="w"> </span><span class="n">dst_path</span>: <span class="nc">WorkingCopyPathBuf</span>::<span class="n">root</span><span class="p">(),</span>
<span class="w"> </span><span class="n">recursive</span>: <span class="nc">true</span><span class="p">,</span>
<span class="p">}]</span>
</code></pre></div>
<p><code>WorkingCopyPatterns</code> will provide an interface to map working copy paths into
repo paths and vice versa. The `WorkingCopy`` trait will apply this mapping to
all snapshot and checkout operations, and jj commands which accept relative
paths will need to be updated to perform working copy path -&gt; repo path
translations as needed. It's not clear at this time <em>which</em> commands will need
changing, as some are more likely to refer to repo paths rather than working
copy paths.</p>
<p>TODO: Expand this section.</p>
<p>In particular, the path rules for sparse patterns will <em>always</em> be repo paths,
not working copy paths. Thus, if the working copy wants to track "foo" and
rename it to "subdir/bar", they must <code>jj sparse set --add foo</code> and
<code>jj map set --from foo --to bar</code>. In other words, the mapping operation can
be thought of as always <em>after</em> the sparse operation.</p>
<h4 id="command-syntax_1">Command Syntax<a class="headerlink" href="#command-syntax_1" title="Permanent link">&para;</a></h4>
<p>New commands will enable editing of the <code>WorkingCopyMapping</code>s:</p>
<p>TODO: Maybe this should be <code>jj workspace map ...</code>?</p>
<ul>
<li><code>jj map list</code> will print all mapping pairs.</li>
<li><code>jj map add --from foo --to bar</code> will add a new mapping to the end of the list.</li>
<li><code>jj map remove --from foo</code> will remove a specific mapping rule.</li>
<li><code>jj map edit</code> will pull up a text editor for manual editing.</li>
</ul>
<p>Like sparse paths, mappings will have a compact text syntax for editing in file
form, or for adding a rule textually on the CLI:</p>
<div class="highlight"><pre><span></span><code>&quot;&lt;from&gt;&quot; -&gt; &quot;&lt;to&gt;&quot; [nonrecursive]
</code></pre></div>
<p>Like sparse paths, mapping rules are defined to apply in <em>order</em> and on any
save operation will be modified to a minimal canonical form. Thus,
<code>jj map set --from "" --to ""</code> will always completely wipe the map.
The first matching rule in reverse list order determines how a particular
repo path should be mapped into the working copy, and likewise how a particular
working copy path should be mapped into the repo. For simplicity, the
'last rule wins' applies both for repo-&gt;WC conversions, as well as WC-&gt;repo
conversions, using the same ordering.</p>
<p>If a working copy mapping places the same repo file at two distinct working
copy paths, snapshotting will fail unless these files are identical. Some
specialized filesystems may even treat these as the 'same' file, allowing this
to work in some cases.</p>
<p>If a working copy mapping places two distinct repo files at the same working
copy path, checkout will fail with an error regardless of equivalence.</p>
<h3 id="versioning-and-storage">Versioning and Storage<a class="headerlink" href="#versioning-and-storage" title="Permanent link">&para;</a></h3>
<p>Updating the active <code>WorkingCopyPatterns</code> for a particular working copy will now
take place in two separate steps: one transaction which updates the op store,
and a separate <code>LockedWorkingCopy</code> operation which actually updates the working
copy. The working copy proto will no longer store <code>WorkingCopyPatterns</code>
directly, instead storing only a <code>WorkingCopyPatternsId</code>. On mismatch with the
current op head, the user will be prompted to run <code>jj workspace update-stale</code>.</p>
<p>This gives the user the ability to update the active <code>WorkingCopyPatterns</code>
whilst not interacting with the local working copy, which is useful for custom
integrations which may not be <em>able</em> to check out particular working copy
patterns due to problems with the backend (encoding, permission errors, etc.). A
bad <code>jj sparse set --add oops</code> command can thus be undone, even via <code>jj op undo</code>
if desired.</p>
<h4 id="view-updates">View Updates<a class="headerlink" href="#view-updates" title="Permanent link">&para;</a></h4>
<p>The View object will be migrated to store working copy patterns via id. The
indirection will save on storage since working copy patterns are not expected to
change very frequently.</p>
<div class="highlight"><pre><span></span><code><span class="c1">// Before:</span>
<span class="k">pub</span><span class="w"> </span><span class="n">wc_commit_ids</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="n">WorkspaceId</span><span class="p">,</span><span class="w"> </span><span class="n">CommitId</span><span class="o">&gt;</span><span class="p">,</span>
<span class="c1">// After:</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">WorkingCopyInfo</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">commit_id</span>: <span class="nc">CommitId</span><span class="p">,</span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">wc_patterns_id</span>: <span class="nc">WorkingCopyPatternsId</span><span class="p">,</span>
<span class="p">}</span>
<span class="o">..</span><span class="p">.</span>
<span class="k">pub</span><span class="w"> </span><span class="n">wc_info</span>: <span class="nc">HashMap</span><span class="o">&lt;</span><span class="n">WorkspaceId</span><span class="p">,</span><span class="w"> </span><span class="n">WorkingCopyInfo</span><span class="o">&gt;</span><span class="p">,</span>
</code></pre></div>
<p>A View object with no stored working copy patterns will be modified at read
time to include the current working copy patterns, thus all <code>read_view</code>
operations will need to pass in the current working copy patterns for a
migration period of at least 6 months. After that, we may choose to auto-fill
missing working copy infos with a default <code>WorkingCopyPatterns</code> as needed.</p>
<h3 id="appendix">Appendix<a class="headerlink" href="#appendix" title="Permanent link">&para;</a></h3>
<h4 id="related-work">Related Work<a class="headerlink" href="#related-work" title="Permanent link">&para;</a></h4>
<p><a href="https://www.perforce.com/manuals/cmdref/Content/CmdRef/views.html">Perforce client maps</a>
are very similar in concept to the entirety of <code>WorkingCopyPatterns</code>, and this
design aims to achieve similar functionality.</p>
<p>The <a href="https://github.com/josh-project/josh">Josh Project</a> implements partial git
clones in a way similar to how sparse patterns try to work.</p>
<h4 id="patterns-via-configuration">Patterns via configuration<a class="headerlink" href="#patterns-via-configuration" title="Permanent link">&para;</a></h4>
<p>There may be some scenarios where it is valuable to configure working copy
patterns via a configuration file, rather than through explicit commands.
Generally this only makes sense for automated repos, with the configuration
coming from outside the repo - there are too many caveats and edge cases if the
configuration comes from inside the repo and/or is fought with by a human.</p>
<p>No configuration syntax is planned at this time but if we add any, we should
probably reuse the compact line syntaxes as much as possible for consistency.</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</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.c011b7c0.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.7389ff0e.min.js"></script>
</body>
</html>