| <!DOCTYPE html> |
| <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> |
| <head> |
| <meta charset="UTF-8"/> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"/> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| <meta name="generator" content="Asciidoctor 2.0.23"/> |
| <title>Terminology</title> |
| <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"/> |
| <style> |
| /*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ |
| /* Uncomment the following line when using as a custom stylesheet */ |
| /* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */ |
| html{font-family:sans-serif;-webkit-text-size-adjust:100%} |
| a{background:none} |
| a:focus{outline:thin dotted} |
| a:active,a:hover{outline:0} |
| h1{font-size:2em;margin:.67em 0} |
| b,strong{font-weight:bold} |
| abbr{font-size:.9em} |
| abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none} |
| dfn{font-style:italic} |
| hr{height:0} |
| mark{background:#ff0;color:#000} |
| code,kbd,pre,samp{font-family:monospace;font-size:1em} |
| pre{white-space:pre-wrap} |
| q{quotes:"\201C" "\201D" "\2018" "\2019"} |
| small{font-size:80%} |
| sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} |
| sup{top:-.5em} |
| sub{bottom:-.25em} |
| img{border:0} |
| svg:not(:root){overflow:hidden} |
| figure{margin:0} |
| audio,video{display:inline-block} |
| audio:not([controls]){display:none;height:0} |
| fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} |
| legend{border:0;padding:0} |
| button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} |
| button,input{line-height:normal} |
| button,select{text-transform:none} |
| button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer} |
| button[disabled],html input[disabled]{cursor:default} |
| input[type=checkbox],input[type=radio]{padding:0} |
| button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} |
| textarea{overflow:auto;vertical-align:top} |
| table{border-collapse:collapse;border-spacing:0} |
| *,::before,::after{box-sizing:border-box} |
| html,body{font-size:100%} |
| body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} |
| a:hover{cursor:pointer} |
| img,object,embed{max-width:100%;height:auto} |
| object,embed{height:100%} |
| img{-ms-interpolation-mode:bicubic} |
| .left{float:left!important} |
| .right{float:right!important} |
| .text-left{text-align:left!important} |
| .text-right{text-align:right!important} |
| .text-center{text-align:center!important} |
| .text-justify{text-align:justify!important} |
| .hide{display:none} |
| img,object,svg{display:inline-block;vertical-align:middle} |
| textarea{height:auto;min-height:50px} |
| select{width:100%} |
| .subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} |
| div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0} |
| a{color:#2156a5;text-decoration:underline;line-height:inherit} |
| a:hover,a:focus{color:#1d4b8f} |
| a img{border:0} |
| p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} |
| p aside{font-size:.875em;line-height:1.35;font-style:italic} |
| h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} |
| h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} |
| h1{font-size:2.125em} |
| h2{font-size:1.6875em} |
| h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} |
| h4,h5{font-size:1.125em} |
| h6{font-size:1em} |
| hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em} |
| em,i{font-style:italic;line-height:inherit} |
| strong,b{font-weight:bold;line-height:inherit} |
| small{font-size:60%;line-height:inherit} |
| code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} |
| ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} |
| ul,ol{margin-left:1.5em} |
| ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0} |
| ul.circle{list-style-type:circle} |
| ul.disc{list-style-type:disc} |
| ul.square{list-style-type:square} |
| ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit} |
| ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} |
| dl dt{margin-bottom:.3125em;font-weight:bold} |
| dl dd{margin-bottom:1.25em} |
| blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} |
| blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} |
| @media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} |
| h1{font-size:2.75em} |
| h2{font-size:2.3125em} |
| h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} |
| h4{font-size:1.4375em}} |
| table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal} |
| table thead,table tfoot{background:#f7f8f7} |
| table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} |
| table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} |
| table tr.even,table tr.alt{background:#f8f8f7} |
| table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6} |
| h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} |
| h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} |
| .center{margin-left:auto;margin-right:auto} |
| .stretch{width:100%} |
| .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} |
| .clearfix::after,.float-group::after{clear:both} |
| :not(pre).nobreak{word-wrap:normal} |
| :not(pre).nowrap{white-space:nowrap} |
| :not(pre).pre-wrap{white-space:pre-wrap} |
| :not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed} |
| pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} |
| pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} |
| pre>code{display:block} |
| pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} |
| em em{font-style:normal} |
| strong strong{font-weight:400} |
| .keyseq{color:rgba(51,51,51,.8)} |
| kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} |
| .keyseq kbd:first-child{margin-left:0} |
| .keyseq kbd:last-child{margin-right:0} |
| .menuseq,.menuref{color:#000} |
| .menuseq b:not(.caret),.menuref{font-weight:inherit} |
| .menuseq{word-spacing:-.02em} |
| .menuseq b.caret{font-size:1.25em;line-height:.8} |
| .menuseq i.caret{font-weight:bold;text-align:center;width:.45em} |
| b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} |
| b.button::before{content:"[";padding:0 3px 0 2px} |
| b.button::after{content:"]";padding:0 2px 0 3px} |
| p a>code:hover{color:rgba(0,0,0,.9)} |
| #header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} |
| #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} |
| #header::after,#content::after,#footnotes::after,#footer::after{clear:both} |
| #content{margin-top:1.25em} |
| #content::before{content:none} |
| #header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} |
| #header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} |
| #header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px} |
| #header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap} |
| #header .details span:first-child{margin-left:-.125em} |
| #header .details span.email a{color:rgba(0,0,0,.85)} |
| #header .details br{display:none} |
| #header .details br+span::before{content:"\00a0\2013\00a0"} |
| #header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} |
| #header .details br+span#revremark::before{content:"\00a0|\00a0"} |
| #header #revnumber{text-transform:capitalize} |
| #header #revnumber::after{content:"\00a0"} |
| #content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} |
| #toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} |
| #toc>ul{margin-left:.125em} |
| #toc ul.sectlevel0>li>a{font-style:italic} |
| #toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} |
| #toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} |
| #toc li{line-height:1.3334;margin-top:.3334em} |
| #toc a{text-decoration:none} |
| #toc a:active{text-decoration:underline} |
| #toctitle{color:#7a2518;font-size:1.2em} |
| @media screen and (min-width:768px){#toctitle{font-size:1.375em} |
| body.toc2{padding-left:15em;padding-right:0} |
| body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} |
| #toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} |
| #toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} |
| #toc.toc2>ul{font-size:.9em;margin-bottom:0} |
| #toc.toc2 ul ul{margin-left:0;padding-left:1em} |
| #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} |
| body.toc2.toc-right{padding-left:0;padding-right:15em} |
| body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} |
| @media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} |
| #toc.toc2{width:20em} |
| #toc.toc2 #toctitle{font-size:1.375em} |
| #toc.toc2>ul{font-size:.95em} |
| #toc.toc2 ul ul{padding-left:1.25em} |
| body.toc2.toc-right{padding-left:0;padding-right:20em}} |
| #content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px} |
| #content #toc>:first-child{margin-top:0} |
| #content #toc>:last-child{margin-bottom:0} |
| #footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em} |
| #footer-text{color:hsla(0,0%,100%,.8);line-height:1.44} |
| #content{margin-bottom:.625em} |
| .sect1{padding-bottom:.625em} |
| @media screen and (min-width:768px){#content{margin-bottom:1.25em} |
| .sect1{padding-bottom:1.25em}} |
| .sect1:last-child{padding-bottom:0} |
| .sect1+.sect1{border-top:1px solid #e7e7e9} |
| #content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} |
| #content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} |
| #content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} |
| #content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} |
| #content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} |
| details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} |
| details{margin-left:1.25rem} |
| details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent} |
| details>summary::-webkit-details-marker{display:none} |
| details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)} |
| details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)} |
| details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem} |
| .admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} |
| table.tableblock.fit-content>caption.title{white-space:nowrap;width:0} |
| .paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)} |
| .admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} |
| .admonitionblock>table td.icon{text-align:center;width:80px} |
| .admonitionblock>table td.icon img{max-width:none} |
| .admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} |
| .admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere} |
| .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} |
| .exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px} |
| .sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px} |
| .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} |
| .exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0} |
| .exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} |
| .literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em} |
| @media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} |
| @media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} |
| .literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8} |
| .literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)} |
| .listingblock>.content{position:relative} |
| .listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5} |
| .listingblock:hover code[data-lang]::before{display:block} |
| .listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5} |
| .listingblock.terminal pre .command:not([data-prompt])::before{content:"$"} |
| .listingblock pre.highlightjs{padding:0} |
| .listingblock pre.highlightjs>code{padding:1em;border-radius:4px} |
| .listingblock pre.prettyprint{border-width:0} |
| .prettyprint{background:#f7f7f8} |
| pre.prettyprint .linenums{line-height:1.45;margin-left:2em} |
| pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0} |
| pre.prettyprint li code[data-lang]::before{opacity:1} |
| pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} |
| table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} |
| table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} |
| table.linenotable td.code{padding-left:.75em} |
| table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} |
| pre.pygments span.linenos{display:inline-block;margin-right:.75em} |
| .quoteblock{margin:0 1em 1.25em 1.5em;display:table} |
| .quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em} |
| .quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} |
| .quoteblock blockquote{margin:0;padding:0;border:0} |
| .quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} |
| .quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} |
| .quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right} |
| .verseblock{margin:0 1em 1.25em} |
| .verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} |
| .verseblock pre strong{font-weight:400} |
| .verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} |
| .quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} |
| .quoteblock .attribution br,.verseblock .attribution br{display:none} |
| .quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)} |
| .quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none} |
| .quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0} |
| .quoteblock.abstract{margin:0 1em 1.25em;display:block} |
| .quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center} |
| .quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf} |
| .quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0} |
| .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem} |
| .quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0} |
| p.tableblock:last-child{margin-bottom:0} |
| td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere} |
| td.tableblock>.content>:last-child{margin-bottom:-1.25em} |
| table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} |
| table.grid-all>*>tr>*{border-width:1px} |
| table.grid-cols>*>tr>*{border-width:0 1px} |
| table.grid-rows>*>tr>*{border-width:1px 0} |
| table.frame-all{border-width:1px} |
| table.frame-ends{border-width:1px 0} |
| table.frame-sides{border-width:0 1px} |
| table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0} |
| table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0} |
| table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0} |
| table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0} |
| table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7} |
| th.halign-left,td.halign-left{text-align:left} |
| th.halign-right,td.halign-right{text-align:right} |
| th.halign-center,td.halign-center{text-align:center} |
| th.valign-top,td.valign-top{vertical-align:top} |
| th.valign-bottom,td.valign-bottom{vertical-align:bottom} |
| th.valign-middle,td.valign-middle{vertical-align:middle} |
| table thead th,table tfoot th{font-weight:bold} |
| tbody tr th{background:#f7f8f7} |
| tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} |
| p.tableblock>code:only-child{background:none;padding:0} |
| p.tableblock{font-size:1em} |
| ol{margin-left:1.75em} |
| ul li ol{margin-left:1.5em} |
| dl dd{margin-left:1.125em} |
| dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} |
| li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} |
| ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none} |
| ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em} |
| ul.unstyled,ol.unstyled{margin-left:0} |
| li>p:empty:only-child::before{content:"";display:inline-block} |
| ul.checklist>li>p:first-child{margin-left:-1em} |
| ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em} |
| ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em} |
| ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em} |
| ul.inline>li{margin-left:1.25em} |
| .unstyled dl dt{font-weight:400;font-style:normal} |
| ol.arabic{list-style-type:decimal} |
| ol.decimal{list-style-type:decimal-leading-zero} |
| ol.loweralpha{list-style-type:lower-alpha} |
| ol.upperalpha{list-style-type:upper-alpha} |
| ol.lowerroman{list-style-type:lower-roman} |
| ol.upperroman{list-style-type:upper-roman} |
| ol.lowergreek{list-style-type:lower-greek} |
| .hdlist>table,.colist>table{border:0;background:none} |
| .hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} |
| td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em} |
| td.hdlist1{font-weight:bold;padding-bottom:1.25em} |
| td.hdlist2{word-wrap:anywhere} |
| .literalblock+.colist,.listingblock+.colist{margin-top:-.5em} |
| .colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top} |
| .colist td:not([class]):first-child img{max-width:none} |
| .colist td:not([class]):last-child{padding:.25em 0} |
| .thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd} |
| .imageblock.left{margin:.25em .625em 1.25em 0} |
| .imageblock.right{margin:.25em 0 1.25em .625em} |
| .imageblock>.title{margin-bottom:0} |
| .imageblock.thumb,.imageblock.th{border-width:6px} |
| .imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} |
| .image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} |
| .image.left{margin-right:.625em} |
| .image.right{margin-left:.625em} |
| a.image{text-decoration:none;display:inline-block} |
| a.image object{pointer-events:none} |
| sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} |
| sup.footnote a,sup.footnoteref a{text-decoration:none} |
| sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline} |
| #footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} |
| #footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} |
| #footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} |
| #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em} |
| #footnotes .footnote:last-of-type{margin-bottom:0} |
| #content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} |
| div.unbreakable{page-break-inside:avoid} |
| .big{font-size:larger} |
| .small{font-size:smaller} |
| .underline{text-decoration:underline} |
| .overline{text-decoration:overline} |
| .line-through{text-decoration:line-through} |
| .aqua{color:#00bfbf} |
| .aqua-background{background:#00fafa} |
| .black{color:#000} |
| .black-background{background:#000} |
| .blue{color:#0000bf} |
| .blue-background{background:#0000fa} |
| .fuchsia{color:#bf00bf} |
| .fuchsia-background{background:#fa00fa} |
| .gray{color:#606060} |
| .gray-background{background:#7d7d7d} |
| .green{color:#006000} |
| .green-background{background:#007d00} |
| .lime{color:#00bf00} |
| .lime-background{background:#00fa00} |
| .maroon{color:#600000} |
| .maroon-background{background:#7d0000} |
| .navy{color:#000060} |
| .navy-background{background:#00007d} |
| .olive{color:#606000} |
| .olive-background{background:#7d7d00} |
| .purple{color:#600060} |
| .purple-background{background:#7d007d} |
| .red{color:#bf0000} |
| .red-background{background:#fa0000} |
| .silver{color:#909090} |
| .silver-background{background:#bcbcbc} |
| .teal{color:#006060} |
| .teal-background{background:#007d7d} |
| .white{color:#bfbfbf} |
| .white-background{background:#fafafa} |
| .yellow{color:#bfbf00} |
| .yellow-background{background:#fafa00} |
| span.icon>.fa{cursor:default} |
| a span.icon>.fa{cursor:inherit} |
| .admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} |
| .admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c} |
| .admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} |
| .admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900} |
| .admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400} |
| .admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000} |
| .conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} |
| .conum[data-value] *{color:#fff!important} |
| .conum[data-value]+b{display:none} |
| .conum[data-value]::after{content:attr(data-value)} |
| pre .conum[data-value]{position:relative;top:-.125em} |
| b.conum *{color:inherit!important} |
| .conum:not([data-value]):empty{display:none} |
| dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} |
| h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em} |
| p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} |
| p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem} |
| p{margin-bottom:1.25rem} |
| .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} |
| .exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc} |
| .print-only{display:none!important} |
| @page{margin:1.25cm .75cm} |
| @media print{*{box-shadow:none!important;text-shadow:none!important} |
| html{font-size:80%} |
| a{color:inherit!important;text-decoration:underline!important} |
| a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} |
| a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} |
| abbr[title]{border-bottom:1px dotted} |
| abbr[title]::after{content:" (" attr(title) ")"} |
| pre,blockquote,tr,img,object,svg{page-break-inside:avoid} |
| thead{display:table-header-group} |
| svg{max-width:100%} |
| p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} |
| h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} |
| #header,#content,#footnotes,#footer{max-width:none} |
| #toc,.sidebarblock,.exampleblock>.content{background:none!important} |
| #toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important} |
| body.book #header{text-align:center} |
| body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em} |
| body.book #header .details{border:0!important;display:block;padding:0!important} |
| body.book #header .details span:first-child{margin-left:0!important} |
| body.book #header .details br{display:block} |
| body.book #header .details br+span::before{content:none!important} |
| body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} |
| body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} |
| .listingblock code[data-lang]::before{display:block} |
| #footer{padding:0 .9375em} |
| .hide-on-print{display:none!important} |
| .print-only{display:block!important} |
| .hide-for-print{display:none!important} |
| .show-for-print{display:inherit!important}} |
| @media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem} |
| .sect1{padding:0!important} |
| .sect1+.sect1{border:0} |
| #footer{background:none} |
| #footer-text{color:rgba(0,0,0,.6);font-size:.9em}} |
| @media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}} |
| </style> |
| </head> |
| <body class="article"> |
| <div id="header"> |
| </div> |
| <div id="content"> |
| <div class="paragraph"> |
| <p>Table of contents:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Terminology</p> |
| </li> |
| <li> |
| <p>Purpose of sparse-checkouts</p> |
| </li> |
| <li> |
| <p>Usecases of primary concern</p> |
| </li> |
| <li> |
| <p>Oversimplified mental models ("Cliff Notes" for this document!)</p> |
| </li> |
| <li> |
| <p>Desired behavior</p> |
| </li> |
| <li> |
| <p>Behavior classes</p> |
| </li> |
| <li> |
| <p>Subcommand-dependent defaults</p> |
| </li> |
| <li> |
| <p>Sparse specification vs. sparsity patterns</p> |
| </li> |
| <li> |
| <p>Implementation Questions</p> |
| </li> |
| <li> |
| <p>Implementation Goals/Plans</p> |
| </li> |
| <li> |
| <p>Known bugs</p> |
| </li> |
| <li> |
| <p>Reference Emails</p> |
| </li> |
| </ul> |
| </div> |
| <div class="sect1"> |
| <h2 id="_terminology">Terminology</h2> |
| <div class="sectionbody"> |
| <div class="dlist"> |
| <dl> |
| <dt class="hdlist1"><strong><code>cone</code> <code>mode</code></strong></dt> |
| <dd> |
| <p>one of two modes for specifying the desired subset of files |
| in a sparse-checkout. In cone-mode, the user specifies |
| directories (getting both everything under that directory as |
| well as everything in leading directories), while in non-cone |
| mode, the user specifies gitignore-style patterns. Controlled |
| by the --[no-]cone option to sparse-checkout init|set.</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>SKIP_WORKTREE</code></strong></dt> |
| <dd> |
| <p>When tracked files do not match the sparse specification and |
| are removed from the working tree, the file in the index is marked |
| with a SKIP_WORKTREE bit. Note that if a tracked file has the |
| SKIP_WORKTREE bit set but the file is later written by the user to |
| the working tree anyway, the SKIP_WORKTREE bit will be cleared at |
| the beginning of any subsequent Git operation.</p> |
| <div class="paragraph"> |
| <p>Most sparse checkout users are unaware of this implementation |
| detail, and the term should generally be avoided in user-facing |
| descriptions and command flags. Unfortunately, prior to the |
| <code>sparse-checkout</code> subcommand this low-level detail was exposed, |
| and as of time of writing, is still exposed in various places.</p> |
| </div> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparse-checkout</code></strong></dt> |
| <dd> |
| <p>a subcommand in git used to reduce the files present in |
| the working tree to a subset of all tracked files. Also, the |
| name of the file in the $GIT_DIR/info directory used to track |
| the sparsity patterns corresponding to the user’s desired |
| subset.</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparse</code> <code>cone</code></strong></dt> |
| <dd> |
| <p>see cone mode</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparse</code> <code>directory</code></strong></dt> |
| <dd> |
| <p>An entry in the index corresponding to a directory, which |
| appears in the index instead of all the files under that directory |
| that would normally appear. See also sparse-index. Something that |
| can cause confusion is that the "sparse directory" does NOT match |
| the sparse specification, i.e. the directory is NOT present in the |
| working tree. May be renamed in the future (e.g. to "skipped |
| directory").</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparse</code> <code>index</code></strong></dt> |
| <dd> |
| <p>A special mode for sparse-checkout that also makes the |
| index sparse by recording a directory entry in lieu of all the |
| files underneath that directory (thus making that a "skipped |
| directory" which unfortunately has also been called a "sparse |
| directory"), and does this for potentially multiple |
| directories. Controlled by the --[no-]sparse-index option to |
| init|set|reapply.</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparsity</code> <code>patterns</code></strong></dt> |
| <dd> |
| <p>patterns from $GIT_DIR/info/sparse-checkout used to |
| define the set of files of interest. A warning: It is easy to |
| over-use this term (or the shortened "patterns" term), for two |
| reasons: (1) users in cone mode specify directories rather than |
| patterns (their directories are transformed into patterns, but |
| users may think you are talking about non-cone mode if you use the |
| word "patterns"), and (2) the sparse specification might |
| transiently differ in the working tree or index from the sparsity |
| patterns (see "Sparse specification vs. sparsity patterns").</p> |
| </dd> |
| <dt class="hdlist1"><strong><code>sparse</code> <code>specification</code></strong></dt> |
| <dd> |
| <p>The set of paths in the user’s area of focus. This |
| is typically just the tracked files that match the sparsity |
| patterns, but the sparse specification can temporarily differ and |
| include additional files. (See also "Sparse specification |
| vs. sparsity patterns")</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>When working with history, the sparse specification is exactly |
| the set of files matching the sparsity patterns.</p> |
| </li> |
| <li> |
| <p>When interacting with the working tree, the sparse specification |
| is the set of tracked files with a clear SKIP_WORKTREE bit or |
| tracked files present in the working copy.</p> |
| </li> |
| <li> |
| <p>When modifying or showing results from the index, the sparse |
| specification is the set of files with a clear SKIP_WORKTREE bit |
| or that differ in the index from HEAD.</p> |
| </li> |
| <li> |
| <p>If working with the index and the working copy, the sparse |
| specification is the union of the paths from above.</p> |
| </li> |
| </ul> |
| </div> |
| </dd> |
| <dt class="hdlist1"><strong><code>vivifying</code></strong></dt> |
| <dd> |
| <p>When a command restores a tracked file to the working tree (and |
| hopefully also clears the SKIP_WORKTREE bit in the index for that |
| file), this is referred to as "vivifying" the file.</p> |
| </dd> |
| </dl> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_purpose_of_sparse_checkouts">Purpose of sparse-checkouts</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>sparse-checkouts exist to allow users to work with a subset of their |
| files.</p> |
| </div> |
| <div class="paragraph"> |
| <p>You can think of sparse-checkouts as subdividing "tracked" files into two |
| categories — a sparse subset, and all the rest. Implementationally, we |
| mark "all the rest" in the index with a SKIP_WORKTREE bit and leave them |
| out of the working tree. The SKIP_WORKTREE files are still tracked, just |
| not present in the working tree.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In the past, sparse-checkouts were defined by "SKIP_WORKTREE means the file |
| is missing from the working tree but pretend the file contents match HEAD". |
| That was not only bogus (it actually meant the file missing from the |
| working tree matched the index rather than HEAD), but it was also a |
| low-level detail which only provided decent behavior for a few commands. |
| There were a surprising number of ways in which that guiding principle gave |
| command results that violated user expectations, and as such was a bad |
| mental model. However, it persisted for many years and may still be found |
| in some corners of the code base.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Anyway, the idea of "working with a subset of files" is simple enough, but |
| there are multiple different high-level usecases which affect how some Git |
| subcommands should behave. Further, even if we only considered one of |
| those usecases, sparse-checkouts can modify different subcommands in over a |
| half dozen different ways. Let’s start by considering the high level |
| usecases:</p> |
| </div> |
| <div class="hdlist"> |
| <table> |
| <tr> |
| <td class="hdlist1"> |
| A) |
| </td> |
| <td class="hdlist2"> |
| <p>Users are <em>only</em> interested in the sparse portion of the repo</p> |
| </td> |
| </tr> |
| <tr> |
| <td class="hdlist1"> |
| A*) |
| </td> |
| <td class="hdlist2"> |
| <p>Users are <em>only</em> interested in the sparse portion of the repo |
| that they have downloaded so far</p> |
| </td> |
| </tr> |
| <tr> |
| <td class="hdlist1"> |
| B) |
| </td> |
| <td class="hdlist2"> |
| <p>Users want a sparse working tree, but are working in a larger whole</p> |
| </td> |
| </tr> |
| <tr> |
| <td class="hdlist1"> |
| C) |
| </td> |
| <td class="hdlist2"> |
| <p>sparse-checkout is a behind-the-scenes implementation detail allowing |
| Git to work with a specially crafted in-house virtual file system; |
| users are actually working with a "full" working tree that is |
| lazily populated, and sparse-checkout helps with the lazy population |
| piece.</p> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>It may be worth explaining each of these in a bit more detail:</p> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_a_users_are_only_interested_in_the_sparse_portion_of_the_repo">(Behavior A) Users are <em>only</em> interested in the sparse portion of the repo</h3> |
| <div class="paragraph"> |
| <p>These folks might know there are other things in the repository, but |
| don’t care. They are uninterested in other parts of the repository, and |
| only want to know about changes within their area of interest. Showing |
| them other files from history (e.g. from diff/log/grep/etc.) is a |
| usability annoyance, potentially a huge one since other changes in |
| history may dwarf the changes they are interested in.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Some of these users also arrive at this usecase from wanting to use partial |
| clones together with sparse checkouts (in a way where they have downloaded |
| blobs within the sparse specification) and do disconnected development. |
| Not only do these users generally not care about other parts of the |
| repository, but consider it a blocker for Git commands to try to operate on |
| those. If commands attempt to access paths in history outside the sparsity |
| specification, then the partial clone will attempt to download additional |
| blobs on demand, fail, and then fail the user’s command. (This may be |
| unavoidable in some cases, e.g. when <code>git</code> <code>merge</code> has non-trivial changes to |
| reconcile outside the sparse specification, but we should limit how often |
| users are forced to connect to the network.)</p> |
| </div> |
| <div class="paragraph"> |
| <p>Also, even for users using partial clones that do not mind being |
| always connected to the network, the need to download blobs as |
| side-effects of various other commands (such as the printed diffstat |
| after a merge or pull) can lead to worries about local repository size |
| growing unnecessarily[10].</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_a_users_are_only_interested_in_the_sparse_portion_of_the_repo_that_they_have_downloaded_so_far_a_variant_on_the_first_usecase">(Behavior A*) Users are <em>only</em> interested in the sparse portion of the repo that they have downloaded so far (a variant on the first usecase)</h3> |
| <div class="paragraph"> |
| <p>This variant is driven by folks who using partial clones together with |
| sparse checkouts and do disconnected development (so far sounding like a |
| subset of behavior A users) and doing so on very large repositories. The |
| reason for yet another variant is that downloading even just the blobs |
| through history within their sparse specification may be too much, so they |
| only download some. They would still like operations to succeed without |
| network connectivity, though, so things like <code>git</code> <code>log</code> <code>-S$</code>{SEARCH_TERM} <code>-p</code> |
| or <code>git</code> <code>grep</code> <code>$</code>{SEARCH_TERM} <code>OLDREV</code> would need to be prepared to provide |
| partial results that depend on what happens to have been downloaded.</p> |
| </div> |
| <div class="paragraph"> |
| <p>This variant could be viewed as Behavior A with the sparse specification |
| for history querying operations modified from "sparsity patterns" to |
| "sparsity patterns limited to the blobs we have already downloaded".</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_b_users_want_a_sparse_working_tree_but_are_working_in_a_larger_whole">(Behavior B) Users want a sparse working tree, but are working in a larger whole</h3> |
| <div class="paragraph"> |
| <p>Stolee described this usecase this way[11]:</p> |
| </div> |
| <div class="paragraph"> |
| <p>"I’m also focused on users that know that they are a part of a larger |
| whole. They know they are operating on a large repository but focus on |
| what they need to contribute their part. I expect multiple "roles" to |
| use very different, almost disjoint parts of the codebase. Some other |
| "architect" users operate across the entire tree or hop between different |
| sections of the codebase as necessary. In this situation, I’m wary of |
| scoping too many features to the sparse-checkout definition, especially |
| "git log," as it can be too confusing to have their view of the codebase |
| depend on your "point of view."</p> |
| </div> |
| <div class="paragraph"> |
| <p>People might also end up wanting behavior B due to complex inter-project |
| dependencies. The initial attempts to use sparse-checkouts usually involve |
| the directories you are directly interested in plus what those directories |
| depend upon within your repository. But there’s a monkey wrench here: if |
| you have integration tests, they invert the hierarchy: to run integration |
| tests, you need not only what you are interested in and its in-tree |
| dependencies, you also need everything that depends upon what you are |
| interested in or that depends upon one of your dependencies…​AND you need |
| all the in-tree dependencies of that expanded group. That can easily |
| change your sparse-checkout into a nearly dense one.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Naturally, that tends to kill the benefits of sparse-checkouts. There are |
| a couple solutions to this conundrum: either avoid grabbing in-repo |
| dependencies (maybe have built versions of your in-repo dependencies pulled |
| from a CI cache somewhere), or say that users shouldn’t run integration |
| tests directly and instead do it on the CI server when they submit a code |
| review. Or do both. Regardless of whether you stub out your in-repo |
| dependencies or stub out the things that depend upon you, there is |
| certainly a reason to want to query and be aware of those other stubbed-out |
| parts of the repository, particularly when the dependencies are complex or |
| change relatively frequently. Thus, for such uses, sparse-checkouts can be |
| used to limit what you directly build and modify, but these users do not |
| necessarily want their sparse checkout paths to limit their queries of |
| versions in history.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Some people may also be interested in behavior B over behavior A simply as |
| a performance workaround: if they are using non-cone mode, then they have |
| to deal with its inherent quadratic performance problems. In that mode, |
| every operation that checks whether paths match the sparsity specification |
| can be expensive. As such, these users may only be willing to pay for |
| those expensive checks when interacting with the working copy, and may |
| prefer getting "unrelated" results from their history queries over having |
| slow commands.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_c_sparse_checkout_is_an_implementational_detail_supporting_a_special_vfs">(Behavior C) sparse-checkout is an implementational detail supporting a special VFS.</h3> |
| <div class="paragraph"> |
| <p>This usecase goes slightly against the traditional definition of |
| sparse-checkout in that it actually tries to present a full or dense |
| checkout to the user. However, this usecase utilizes the same underlying |
| technical underpinnings in a new way which does provide some performance |
| advantages to users. The basic idea is that a company can have an in-house |
| Git-aware Virtual File System which pretends all files are present in the |
| working tree, by intercepting all file system accesses and using those to |
| fetch and write accessed files on demand via partial clones. The VFS uses |
| sparse-checkout to prevent Git from writing or paying attention to many |
| files, and manually updates the sparse checkout patterns itself based on |
| user access and modification of files in the working tree. See commit |
| ecc7c8841d ("repo_read_index: add config to expect files outside sparse |
| patterns", 2022-02-25) and the link at [17] for a more detailed description |
| of such a VFS.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The biggest difference here is that users are completely unaware that the |
| sparse-checkout machinery is even in use. The sparse patterns are not |
| specified by the user but rather are under the complete control of the VFS |
| (and the patterns are updated frequently and dynamically by it). The user |
| will perceive the checkout as dense, and commands should thus behave as if |
| all files are present.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_usecases_of_primary_concern">Usecases of primary concern</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Most of the rest of this document will focus on Behavior A and Behavior |
| B. Some notes about the other two cases and why we are not focusing on |
| them:</p> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_a">(Behavior A*)</h3> |
| <div class="paragraph"> |
| <p>Supporting this usecase is estimated to be difficult and a lot of work. |
| There are no plans to implement it currently, but it may be a potential |
| future alternative. Knowing about the existence of additional alternatives |
| may affect our choice of command line flags (e.g. if we need tri-state or |
| quad-state flags rather than just binary flags), so it was still important |
| to at least note.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Further, I believe the descriptions below for Behavior A are probably still |
| valid for this usecase, with the only exception being that it redefines the |
| sparse specification to restrict it to already-downloaded blobs. The hard |
| part is in making commands capable of respecting that modified definition.</p> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_behavior_c">(Behavior C)</h3> |
| <div class="paragraph"> |
| <p>This usecase violates some of the early sparse-checkout documented |
| assumptions (since files marked as SKIP_WORKTREE will be displayed to users |
| as present in the working tree). That violation may mean various |
| sparse-checkout related behaviors are not well suited to this usecase and |
| we may need tweaks — to both documentation and code — to handle it. |
| However, this usecase is also perhaps the simplest model to support in that |
| everything behaves like a dense checkout with a few exceptions (e.g. branch |
| checkouts and switches write fewer things, knowing the VFS will lazily |
| write the rest on an as-needed basis).</p> |
| </div> |
| <div class="paragraph"> |
| <p>Since there is no publicly available VFS-related code for folks to try, |
| the number of folks who can test such a usecase is limited.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The primary reason to note the Behavior C usecase is that as we fix things |
| to better support Behaviors A and B, there may be additional places where |
| we need to make tweaks allowing folks in this usecase to get the original |
| non-sparse treatment. For an example, see ecc7c8841d ("repo_read_index: |
| add config to expect files outside sparse patterns", 2022-02-25). The |
| secondary reason to note Behavior C, is so that folks taking advantage of |
| Behavior C do not assume they are part of the Behavior B camp and propose |
| patches that break things for the real Behavior B folks.</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_oversimplified_mental_models">Oversimplified mental models</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>An oversimplification of the differences in the above behaviors is:</p> |
| </div> |
| <div class="dlist"> |
| <dl> |
| <dt class="hdlist1">(Behavior A)</dt> |
| <dd> |
| <p>Restrict worktree and history operations to sparse specification</p> |
| </dd> |
| <dt class="hdlist1">(Behavior B)</dt> |
| <dd> |
| <p>Restrict worktree operations to sparse specification; have any |
| history operations work across all files</p> |
| </dd> |
| <dt class="hdlist1">(Behavior C)</dt> |
| <dd> |
| <p>Do not restrict either worktree or history operations to the |
| sparse specification…​with the exception of branch checkouts or |
| switches which avoid writing files that will match the index so |
| they can later lazily be populated instead.</p> |
| </dd> |
| </dl> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_desired_behavior">Desired behavior</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>As noted previously, despite the simple idea of just working with a subset |
| of files, there are a range of different behavioral changes that need to be |
| made to different subcommands to work well with such a feature. See |
| [1,2,3,4,5,6,7,8,9,10] for various examples. In particular, at [2], we saw |
| that mere composition of other commands that individually worked correctly |
| in a sparse-checkout context did not imply that the higher level command |
| would work correctly; it sometimes requires further tweaks. So, |
| understanding these differences can be beneficial.</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Commands behaving the same regardless of high-level use-case</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>commands that only look at files within the sparsity specification</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>diff (without --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>grep (without --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>diff-files</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>commands that restore files to the working tree that match sparsity |
| patterns, and remove unmodified files that don’t match those |
| patterns:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>switch</p> |
| </li> |
| <li> |
| <p>checkout (the switch-like half)</p> |
| </li> |
| <li> |
| <p>read-tree</p> |
| </li> |
| <li> |
| <p>reset --hard</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>commands that write conflicted files to the working tree, but otherwise |
| will omit writing files to the working tree that do not match the |
| sparsity patterns:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>merge</p> |
| </li> |
| <li> |
| <p>rebase</p> |
| </li> |
| <li> |
| <p>cherry-pick</p> |
| </li> |
| <li> |
| <p>revert</p> |
| </li> |
| <li> |
| <p><code>am</code> and <code>apply</code> <code>--cached</code> should probably be in this section but |
| are buggy (see the "Known bugs" section below)</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>The behavior for these commands somewhat depends upon the merge |
| strategy being used:</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p><code>ort</code> behaves as described above</p> |
| </li> |
| <li> |
| <p><code>octopus</code> and <code>resolve</code> will always vivify any file changed in the merge |
| relative to the first parent, which is rather suboptimal.</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>It is also important to note that these commands WILL update the index |
| outside the sparse specification relative to when the operation began, |
| BUT these commands often make a commit just before or after such that |
| by the end of the operation there is no change to the index outside the |
| sparse specification. Of course, if the operation hits conflicts or |
| does not make a commit, then these operations clearly can modify the |
| index outside the sparse specification.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Finally, it is important to note that at least the first four of these |
| commands also try to remove differences between the sparse |
| specification and the sparsity patterns (much like the commands in the |
| previous section).</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>commands that always ignore sparsity since commits must be full-tree</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>archive</p> |
| </li> |
| <li> |
| <p>bundle</p> |
| </li> |
| <li> |
| <p>commit</p> |
| </li> |
| <li> |
| <p>format-patch</p> |
| </li> |
| <li> |
| <p>fast-export</p> |
| </li> |
| <li> |
| <p>fast-import</p> |
| </li> |
| <li> |
| <p>commit-tree</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>commands that write any modified file to the working tree (conflicted |
| or not, and whether those paths match sparsity patterns or not):</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>stash</p> |
| </li> |
| <li> |
| <p>apply (without <code>--index</code> or <code>--cached</code>)</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands that may slightly differ for behavior A vs. behavior B:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this category behave mostly the same between the two |
| behaviors, but may differ in verbosity and types of warning and error |
| messages.</pre> |
| </div> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>commands that make modifications to which files are tracked:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>add</p> |
| </li> |
| <li> |
| <p>rm</p> |
| </li> |
| <li> |
| <p>mv</p> |
| </li> |
| <li> |
| <p>update-index</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>The fact that files can move between the 'tracked' and 'untracked' |
| categories means some commands will have to treat untracked files |
| differently. But if we have to treat untracked files differently, |
| then additional commands may also need changes:</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>status</p> |
| </li> |
| <li> |
| <p>clean</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>In particular, `status` may need to report any untracked files outside |
| the sparsity specification as an erroneous condition (especially to |
| avoid the user trying to `git add` them, forcing `git add` to display |
| an error).</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>It's not clear to me exactly how (or even if) `clean` would change, |
| but it's the other command that also affects untracked files.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>`update-index` may be slightly special. Its --[no-]skip-worktree flag |
| may need to ignore the sparse specification by its nature. Also, its |
| current --[no-]ignore-skip-worktree-entries default is totally bogus.</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>commands for manually tweaking paths in both the index and the working tree</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>restore</code></p> |
| </li> |
| <li> |
| <p>the restore-like half of <code>checkout</code></p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>These commands should be similar to add/rm/mv in that they should |
| only operate on the sparse specification by default, and require a |
| special flag to operate on all files.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Also, note that these commands currently have a number of issues (see |
| the "Known bugs" section below)</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands that significantly differ for behavior A vs. behavior B:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>commands that query history</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>diff (with --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>grep (with --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>show (when given commit arguments)</p> |
| </li> |
| <li> |
| <p>blame (only matters when one or more -C flags are passed)</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>and annotate</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>log</p> |
| </li> |
| <li> |
| <p>whatchanged (may not exist anymore)</p> |
| </li> |
| <li> |
| <p>ls-files</p> |
| </li> |
| <li> |
| <p>diff-index</p> |
| </li> |
| <li> |
| <p>diff-tree</p> |
| </li> |
| <li> |
| <p>ls-tree</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Note: for log and whatchanged, revision walking logic is unaffected |
| but displaying of patches is affected by scoping the command to the |
| sparse-checkout. (The fact that revision walking is unaffected is |
| why rev-list, shortlog, show-branch, and bisect are not in this |
| list.)</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>ls-files may be slightly special in that e.g. `git ls-files -t` is |
| often used to see what is sparse and what is not. Perhaps -t should |
| always work on the full tree?</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands I don’t know how to classify</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>range-diff</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Is this like `log` or `format-patch`?</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>cherry</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>See range-diff</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands unaffected by sparse-checkouts</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>shortlog</p> |
| </li> |
| <li> |
| <p>show-branch</p> |
| </li> |
| <li> |
| <p>rev-list</p> |
| </li> |
| <li> |
| <p>bisect</p> |
| </li> |
| <li> |
| <p>branch</p> |
| </li> |
| <li> |
| <p>describe</p> |
| </li> |
| <li> |
| <p>fetch</p> |
| </li> |
| <li> |
| <p>gc</p> |
| </li> |
| <li> |
| <p>init</p> |
| </li> |
| <li> |
| <p>maintenance</p> |
| </li> |
| <li> |
| <p>notes</p> |
| </li> |
| <li> |
| <p>pull (merge & rebase have the necessary changes)</p> |
| </li> |
| <li> |
| <p>push</p> |
| </li> |
| <li> |
| <p>submodule</p> |
| </li> |
| <li> |
| <p>tag</p> |
| </li> |
| <li> |
| <p>config</p> |
| </li> |
| <li> |
| <p>filter-branch (works in separate checkout without sparse-checkout setup)</p> |
| </li> |
| <li> |
| <p>pack-refs</p> |
| </li> |
| <li> |
| <p>prune</p> |
| </li> |
| <li> |
| <p>remote</p> |
| </li> |
| <li> |
| <p>repack</p> |
| </li> |
| <li> |
| <p>replace</p> |
| </li> |
| <li> |
| <p>bugreport</p> |
| </li> |
| <li> |
| <p>count-objects</p> |
| </li> |
| <li> |
| <p>fsck</p> |
| </li> |
| <li> |
| <p>gitweb</p> |
| </li> |
| <li> |
| <p>help</p> |
| </li> |
| <li> |
| <p>instaweb</p> |
| </li> |
| <li> |
| <p>merge-tree (doesn’t touch worktree or index, and merges always compute full-tree)</p> |
| </li> |
| <li> |
| <p>rerere</p> |
| </li> |
| <li> |
| <p>verify-commit</p> |
| </li> |
| <li> |
| <p>verify-tag</p> |
| </li> |
| <li> |
| <p>commit-graph</p> |
| </li> |
| <li> |
| <p>hash-object</p> |
| </li> |
| <li> |
| <p>index-pack</p> |
| </li> |
| <li> |
| <p>mktag</p> |
| </li> |
| <li> |
| <p>mktree</p> |
| </li> |
| <li> |
| <p>multi-pack-index</p> |
| </li> |
| <li> |
| <p>pack-objects</p> |
| </li> |
| <li> |
| <p>prune-packed</p> |
| </li> |
| <li> |
| <p>symbolic-ref</p> |
| </li> |
| <li> |
| <p>unpack-objects</p> |
| </li> |
| <li> |
| <p>update-ref</p> |
| </li> |
| <li> |
| <p>write-tree (operates on index, possibly optimized to use sparse dir entries)</p> |
| </li> |
| <li> |
| <p>for-each-ref</p> |
| </li> |
| <li> |
| <p>get-tar-commit-id</p> |
| </li> |
| <li> |
| <p>ls-remote</p> |
| </li> |
| <li> |
| <p>merge-base (merges are computed full tree, so merge base should be too)</p> |
| </li> |
| <li> |
| <p>name-rev</p> |
| </li> |
| <li> |
| <p>pack-redundant</p> |
| </li> |
| <li> |
| <p>rev-parse</p> |
| </li> |
| <li> |
| <p>show-index</p> |
| </li> |
| <li> |
| <p>show-ref</p> |
| </li> |
| <li> |
| <p>unpack-file</p> |
| </li> |
| <li> |
| <p>var</p> |
| </li> |
| <li> |
| <p>verify-pack</p> |
| </li> |
| <li> |
| <p><Everything under <em>Interacting with Others</em> in <em>git help --all</em>></p> |
| </li> |
| <li> |
| <p><Everything under <em>Low-level…​Syncing</em> in <em>git help --all</em>></p> |
| </li> |
| <li> |
| <p><Everything under <em>Low-level…​Internal Helpers</em> in <em>git help --all</em>></p> |
| </li> |
| <li> |
| <p><Everything under <em>External commands</em> in <em>git help --all</em>></p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands that might be affected, but who cares?</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>merge-file</p> |
| </li> |
| <li> |
| <p>merge-index</p> |
| </li> |
| <li> |
| <p>gitk?</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_behavior_classes">Behavior classes</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>From the above there are a few classes of behavior:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>"restrict"</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this class only read or write files in the working tree |
| within the sparse specification.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>When moving to a new commit (e.g. switch, reset --hard), these commands |
| may update index files outside the sparse specification as of the start |
| of the operation, but by the end of the operation those index files |
| will match HEAD again and thus those files will again be outside the |
| sparse specification.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>When paths are explicitly specified, these paths are intersected with |
| the sparse specification and will only operate on such paths. |
| (e.g. `git restore [--staged] -- '*.png'`, `git reset -p -- '*.md'`)</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Some of these commands may also attempt, at the end of their operation, |
| to cull transient differences between the sparse specification and the |
| sparsity patterns (see "Sparse specification vs. sparsity patterns" for |
| details, but this basically means either removing unmodified files not |
| matching the sparsity patterns and marking those files as |
| SKIP_WORKTREE, or vivifying files that match the sparsity patterns and |
| marking those files as !SKIP_WORKTREE).</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>"restrict modulo conflicts"</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this class generally behave like the "restrict" class, |
| except that:</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>(1) they will ignore the sparse specification and write files with |
| conflicts to the working tree (thus temporarily expanding the |
| sparse specification to include such files.) |
| (2) they are grouped with commands which move to a new commit, since |
| they often create a commit and then move to it, even though we |
| know there are many exceptions to moving to the new commit. (For |
| example, the user may rebase a commit that becomes empty, or have |
| a cherry-pick which conflicts, or a user could run `merge |
| --no-commit`, and we also view `apply --index` kind of like `am |
| --no-commit`.) As such, these commands can make changes to index |
| files outside the sparse specification, though they'll mark such |
| files with SKIP_WORKTREE.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>"restrict also specially applied to untracked files"</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this class generally behave like the "restrict" class, |
| except that they have to handle untracked files differently too, often |
| because these commands are dealing with files changing state between |
| 'tracked' and 'untracked'. Often, this may mean printing an error |
| message if the command had nothing to do, but the arguments may have |
| referred to files whose tracked-ness state could have changed were it |
| not for the sparsity patterns excluding them.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>"no restrict"</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this class ignore the sparse specification entirely.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>"restrict or no restrict dependent upon behavior A vs. behavior B"</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Commands in this class behave like "no restrict" for folks in the |
| behavior B camp, and like "restrict" for folks in the behavior A camp. |
| However, when behaving like "restrict" a warning of some sort might be |
| provided that history queries have been limited by the sparse-checkout |
| specification.</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_subcommand_dependent_defaults">Subcommand-dependent defaults</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Note that we have different defaults depending on the command for the |
| desired behavior :</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Commands defaulting to "restrict":</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>diff-files</p> |
| </li> |
| <li> |
| <p>diff (without --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>grep (without --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>switch</p> |
| </li> |
| <li> |
| <p>checkout (the switch-like half)</p> |
| </li> |
| <li> |
| <p>reset (<commit>)</p> |
| </li> |
| <li> |
| <p>restore</p> |
| </li> |
| <li> |
| <p>checkout (the restore-like half)</p> |
| </li> |
| <li> |
| <p>checkout-index</p> |
| </li> |
| <li> |
| <p>reset (with pathspec)</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>This behavior makes sense; these interact with the working tree.</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands defaulting to "restrict modulo conflicts":</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>merge</p> |
| </li> |
| <li> |
| <p>rebase</p> |
| </li> |
| <li> |
| <p>cherry-pick</p> |
| </li> |
| <li> |
| <p>revert</p> |
| </li> |
| <li> |
| <p>am</p> |
| </li> |
| <li> |
| <p>apply --index (which is kind of like an <code>am</code> <code>--no-commit</code>)</p> |
| </li> |
| <li> |
| <p>read-tree (especially with -m or -u; is kind of like a --no-commit merge)</p> |
| </li> |
| <li> |
| <p>reset (<tree-ish>, due to similarity to read-tree)</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>These also interact with the working tree, but require slightly |
| different behavior either so that (a) conflicts can be resolved or (b) |
| because they are kind of like a merge-without-commit operation.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>(See also the "Known bugs" section below regarding `am` and `apply`)</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands defaulting to "no restrict":</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>archive</p> |
| </li> |
| <li> |
| <p>bundle</p> |
| </li> |
| <li> |
| <p>commit</p> |
| </li> |
| <li> |
| <p>format-patch</p> |
| </li> |
| <li> |
| <p>fast-export</p> |
| </li> |
| <li> |
| <p>fast-import</p> |
| </li> |
| <li> |
| <p>commit-tree</p> |
| </li> |
| <li> |
| <p>stash</p> |
| </li> |
| <li> |
| <p>apply (without <code>--index</code>)</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>These have completely different defaults and perhaps deserve the most |
| detailed explanation:</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>In the case of commands in the first group (format-patch, |
| fast-export, bundle, archive, etc.), these are commands for |
| communicating history, which will be broken if they restrict to a |
| subset of the repository. As such, they operate on full paths and |
| have no `--restrict` option for overriding. Some of these commands may |
| take paths for manually restricting what is exported, but it needs to |
| be very explicit.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>In the case of stash, it needs to vivify files to avoid losing the |
| user's changes.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>In the case of apply without `--index`, that command needs to update |
| the working tree without the index (or the index without the working |
| tree if `--cached` is passed), and if we restrict those updates to the |
| sparse specification then we'll lose changes from the user.</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Commands defaulting to "restrict also specially applied to untracked files":</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>add</p> |
| </li> |
| <li> |
| <p>rm</p> |
| </li> |
| <li> |
| <p>mv</p> |
| </li> |
| <li> |
| <p>update-index</p> |
| </li> |
| <li> |
| <p>status</p> |
| </li> |
| <li> |
| <p>clean (?)</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre> Our original implementation for the first three of these commands was |
| "no restrict", but it had some severe usability issues: |
| |
| * `git add <somefile>` if honored and outside the sparse |
| specification, can result in the file randomly disappearing later |
| when some subsequent command is run (since various commands |
| automatically clean up unmodified files outside the sparse |
| specification). |
| * `git rm '*.jpg'` could very negatively surprise users if it deletes |
| files outside the range of the user's interest. |
| * `git mv` has similar surprises when moving into or out of the cone, |
| so best to restrict by default |
| |
| So, we switched `add` and `rm` to default to "restrict", which made |
| usability problems much less severe and less frequent, but we still got |
| complaints because commands like: |
| |
| git add <file-outside-sparse-specification> |
| git rm <file-outside-sparse-specification> |
| |
| would silently do nothing. We should instead print an error in those |
| cases to get usability right. |
| |
| update-index needs to be updated to match, and status and maybe clean |
| also need to be updated to specially handle untracked paths. |
| |
| There may be a difference in here between behavior A and behavior B in |
| terms of verboseness of errors or additional warnings.</pre> |
| </div> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Commands falling under "restrict or no restrict dependent upon behavior |
| A vs. behavior B"</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>diff (with --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>grep (with --cached or REVISION arguments)</p> |
| </li> |
| <li> |
| <p>show (when given commit arguments)</p> |
| </li> |
| <li> |
| <p>blame (only matters when one or more -C flags passed)</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>and annotate</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>log</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>and variants: shortlog, gitk, show-branch, whatchanged, rev-list</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>ls-files</p> |
| </li> |
| <li> |
| <p>diff-index</p> |
| </li> |
| <li> |
| <p>diff-tree</p> |
| </li> |
| <li> |
| <p>ls-tree</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>For now, we default to behavior B for these, which want a default of |
| "no restrict".</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Note that two of these commands -- diff and grep -- also appeared in a |
| different list with a default of "restrict", but only when limited to |
| searching the working tree. The working tree vs. history distinction |
| is fundamental in how behavior B operates, so this is expected. Note, |
| though, that for diff and grep with --cached, when doing "restrict" |
| behavior, the difference between sparse specification and sparsity |
| patterns is important to handle.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>"restrict" may make more sense as the long term default for these[12]. |
| Also, supporting "restrict" for these commands might be a fair amount |
| of work to implement, meaning it might be implemented over multiple |
| releases. If that behavior were the default in the commands that |
| supported it, that would force behavior B users to need to learn to |
| slowly add additional flags to their commands, depending on git |
| version, to get the behavior they want. That gradual switchover would |
| be painful, so we should avoid it at least until it's fully |
| implemented.</pre> |
| </div> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_sparse_specification_vs_sparsity_patterns">Sparse specification vs. sparsity patterns</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>In a well-behaved situation, the sparse specification is given directly |
| by the $GIT_DIR/info/sparse-checkout file. However, it can transiently |
| diverge for a few reasons:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>needing to resolve conflicts (merging will vivify conflicted files)</p> |
| </li> |
| <li> |
| <p>running Git commands that implicitly vivify files (e.g. "git stash apply")</p> |
| </li> |
| <li> |
| <p>running Git commands that explicitly vivify files (e.g. "git checkout |
| --ignore-skip-worktree-bits FILENAME")</p> |
| </li> |
| <li> |
| <p>other commands that write to these files (perhaps a user copies it |
| from elsewhere)</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>For the last item, note that we do automatically clear the SKIP_WORKTREE |
| bit for files that are present in the working tree. This has been true |
| since 82386b4496 ("Merge branch <em>en/present-despite-skipped</em>", |
| 2022-03-09)</p> |
| </div> |
| <div class="paragraph"> |
| <p>However, such a situation is transient because:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Such transient differences can and will be automatically removed as |
| a side-effect of commands which call unpack_trees() (checkout, |
| merge, reset, etc.).</p> |
| </li> |
| <li> |
| <p>Users can also request such transient differences be corrected via |
| running <code>git</code> <code>sparse-checkout</code> <code>reapply</code>. Various places recommend |
| running that command.</p> |
| </li> |
| <li> |
| <p>Additional commands are also welcome to implicitly fix these |
| differences; we may add more in the future.</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>While we avoid dropping unstaged changes or files which have conflicts, |
| we otherwise aggressively try to fix these transient differences. If |
| users want these differences to persist, they should run the <code>set</code> or |
| <code>add</code> subcommands of <code>git</code> <code>sparse-checkout</code> to reflect their intended |
| sparse specification.</p> |
| </div> |
| <div class="paragraph"> |
| <p>However, when we need to do a query on history restricted to the |
| "relevant subset of files" such a transiently expanded sparse |
| specification is ignored. There are a couple reasons for this:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>The behavior wanted when doing something like |
| git grep expression REVISION |
| is roughly what the users would expect from |
| git checkout REVISION && git grep expression |
| (modulo a "REVISION:" prefix), which has a couple ramifications:</p> |
| </li> |
| <li> |
| <p>REVISION may have paths not in the current index, so there is no |
| path we can consult for a SKIP_WORKTREE setting for those paths.</p> |
| </li> |
| <li> |
| <p>Since <code>checkout</code> is one of those commands that tries to remove |
| transient differences in the sparse specification, it makes sense |
| to use the corrected sparse specification |
| (i.e. $GIT_DIR/info/sparse-checkout) rather than attempting to |
| consult SKIP_WORKTREE anyway.</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>So, a transiently expanded (or restricted) sparse specification applies to |
| the working tree, but not to history queries where we always use the |
| sparsity patterns. (See [16] for an early discussion of this.)</p> |
| </div> |
| <div class="paragraph"> |
| <p>Similar to a transiently expanded sparse specification of the working tree |
| based on additional files being present in the working tree, we also need |
| to consider additional files being modified in the index. In particular, |
| if the user has staged changes to files (relative to HEAD) that do not |
| match the sparsity patterns, and the file is not present in the working |
| tree, we still want to consider the file part of the sparse specification |
| if we are specifically performing a query related to the index (e.g. git |
| diff --cached [REVISION], git diff-index [REVISION], git restore --staged |
| --source=REVISION — PATHS, etc.) Note that a transiently expanded sparse |
| specification for the index usually only matters under behavior A, since |
| under behavior B index operations are lumped with history and tend to |
| operate full-tree.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_implementation_questions">Implementation Questions</h2> |
| <div class="sectionbody"> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Do the options --scope={sparse,all} sound good to others? Are there better options?</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Names in use, or appearing in patches, or previously suggested:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>--sparse/--dense</p> |
| </li> |
| <li> |
| <p>--ignore-skip-worktree-bits</p> |
| </li> |
| <li> |
| <p>--ignore-skip-worktree-entries</p> |
| </li> |
| <li> |
| <p>--ignore-sparsity</p> |
| </li> |
| <li> |
| <p>--[no-]restrict-to-sparse-paths</p> |
| </li> |
| <li> |
| <p>--full-tree/--sparse-tree</p> |
| </li> |
| <li> |
| <p>--[no-]restrict</p> |
| </li> |
| <li> |
| <p>--scope={sparse,all}</p> |
| </li> |
| <li> |
| <p>--focus/--unfocus</p> |
| </li> |
| <li> |
| <p>--limit/--unlimited</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>Rationale making me lean slightly towards --scope={sparse,all}:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>We want a name that works for many commands, so we need a name that |
| does not conflict</p> |
| </li> |
| <li> |
| <p>We know that we have more than two possible usecases, so it is best |
| to avoid a flag that appears to be binary.</p> |
| </li> |
| <li> |
| <p>--scope={sparse,all} isn’t overly long and seems relatively |
| explanatory</p> |
| </li> |
| <li> |
| <p><code>--sparse</code>, as used in add/rm/mv, is totally backwards for |
| grep/log/etc. Changing the meaning of <code>--sparse</code> for these |
| commands would fix the backwardness, but possibly break existing |
| scripts. Using a new name pairing would allow us to treat |
| <code>--sparse</code> in these commands as a deprecated alias.</p> |
| </li> |
| <li> |
| <p>There is a different <code>--sparse</code>/<code>--dense</code> pair for commands using |
| revision machinery, so using that naming might cause confusion</p> |
| </li> |
| <li> |
| <p>There is also a <code>--sparse</code> in both pack-objects and show-branch, which |
| don’t conflict but do suggest that <code>--sparse</code> is overloaded</p> |
| </li> |
| <li> |
| <p>The name --ignore-skip-worktree-bits is a double negative, is |
| quite a mouthful, refers to an implementation detail that many |
| users may not be familiar with, and we’d need a negation for it |
| which would probably be even more ridiculously long. (But we |
| can make --ignore-skip-worktree-bits a deprecated alias for |
| --no-restrict.)</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>If a config option is added (sparse.scope?) what should the values and |
| description be? "sparse" (behavior A), "worktree-sparse-history-dense" |
| (behavior B), "dense" (behavior C)? There’s a risk of confusion, |
| because even for Behaviors A and B we want some commands to be |
| full-tree and others to operate sparsely, so the wording may need to be |
| more tied to the usecases and somehow explain that. Also, right now, |
| the primary difference we are focusing is just the history-querying |
| commands (log/diff/grep). Previous config suggestion here: [13]</p> |
| </li> |
| <li> |
| <p>Is <code>--no-expand</code> a good alias for ls-files’s <code>--sparse</code> option? |
| (<code>--sparse</code> does not map to either <code>--scope=sparse</code> or <code>--scope=all</code>, |
| because in non-cone mode it does nothing and in cone-mode it shows the |
| sparse directory entries which are technically outside the sparse |
| specification)</p> |
| </li> |
| <li> |
| <p>Under Behavior A:</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Does ls-files' <code>--no-expand</code> override the default <code>--scope=all</code>, or |
| does it need an extra flag?</p> |
| </li> |
| <li> |
| <p>Does ls-files' <code>-t</code> option imply <code>--scope=all</code>?</p> |
| </li> |
| <li> |
| <p>Does update-index’s <code>--</code>[<code>no-</code>]<code>skip-worktree</code> option imply <code>--scope=all</code>?</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| <li> |
| <p>sparse-checkout: once behavior A is fully implemented, should we take |
| an interim measure to ease people into switching the default? Namely, |
| if folks are not already in a sparse checkout, then require |
| <code>sparse-checkout</code> <code>init/set</code> to take a |
| <code>--set-scope=</code>(<code>sparse</code>|<code>worktree-sparse-history-dense</code>|<code>dense</code>) flag (which |
| would set sparse.scope according to the setting given), and throw an |
| error if the flag is not provided? That error would be a great place |
| to warn folks that the default may change in the future, and get them |
| used to specifying what they want so that the eventual default switch |
| is seamless for them.</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_implementation_goalsplans">Implementation Goals/Plans</h2> |
| <div class="sectionbody"> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Get buy-in on this document in general.</p> |
| </li> |
| <li> |
| <p>Figure out answers to the <em>Implementation Questions</em> sections (above)</p> |
| </li> |
| <li> |
| <p>Fix bugs in the <em>Known bugs</em> section (below)</p> |
| </li> |
| <li> |
| <p>Provide some kind of method for backfilling the blobs within the sparse |
| specification in a partial clone</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>[Below here is kind of spitballing since the first two haven't been resolved]</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>update-index: flip the default to --no-ignore-skip-worktree-entries, |
| nuke this stupid "Oh, there’s a bug? Let me add a flag to let users |
| request that they not trigger this bug." flag</p> |
| </li> |
| <li> |
| <p>Flags & Config</p> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>Make <code>--sparse</code> in add/rm/mv a deprecated alias for <code>--scope=all</code></p> |
| </li> |
| <li> |
| <p>Make <code>--ignore-skip-worktree-bits</code> in checkout-index/checkout/restore |
| a deprecated aliases for <code>--scope=all</code></p> |
| </li> |
| <li> |
| <p>Create config option (sparse.scope?), tie it to the "Cliff notes" |
| overview</p> |
| </li> |
| <li> |
| <p>Add --scope=sparse (and --scope=all) flag to each of the history querying |
| commands. IMPORTANT: make sure diff machinery changes don’t mess with |
| format-patch, fast-export, etc.</p> |
| </li> |
| </ul> |
| </div> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_known_bugs">Known bugs</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>This list used to be a lot longer (see e.g. [1,2,3,4,5,6,7,8,9]), but we’ve |
| been working on it.</p> |
| </div> |
| <div class="olist arabic"> |
| <ol class="arabic"> |
| <li> |
| <p>Behavior A is not well supported in Git. (Behavior B didn’t used to |
| be either, but was the easier of the two to implement.)</p> |
| </li> |
| <li> |
| <p>am and apply:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>apply, without `--index` or `--cached`, relies on files being present |
| in the working copy, and also writes to them unconditionally. As |
| such, it should first check for the files' presence, and if found to |
| be SKIP_WORKTREE, then clear the bit and vivify the paths, then do |
| its work. Currently, it just throws an error.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>apply, with either `--cached` or `--index`, will not preserve the |
| SKIP_WORKTREE bit. This is fine if the file has conflicts, but |
| otherwise SKIP_WORKTREE bits should be preserved for --cached and |
| probably also for --index.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>am, if there are no conflicts, will vivify files and fail to preserve |
| the SKIP_WORKTREE bit. If there are conflicts and `-3` is not |
| specified, it will vivify files and then complain the patch doesn't |
| apply. If there are conflicts and `-3` is specified, it will vivify |
| files and then complain that those vivified files would be |
| overwritten by merge.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>reset --hard:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>reset --hard provides confusing error message (works correctly, but |
| misleads the user into believing it didn't):</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>$ touch addme |
| $ git add addme |
| $ git ls-files -t |
| H addme |
| H tracked |
| S tracked-but-maybe-skipped |
| $ git reset --hard # usually works great |
| error: Path 'addme' not uptodate; will not remove from working tree. |
| HEAD is now at bdbbb6f third |
| $ git ls-files -t |
| H tracked |
| S tracked-but-maybe-skipped |
| $ ls -1 |
| tracked</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>`git reset --hard` DID remove addme from the index and the working tree, contrary |
| to the error message, but in line with how reset --hard should behave.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>read-tree</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>`read-tree` doesn't apply the 'SKIP_WORKTREE' bit to *any* of the |
| entries it reads into the index, resulting in all your files suddenly |
| appearing to be "deleted".</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>Checkout, restore:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>These command do not handle path & revision arguments appropriately:</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>$ ls |
| tracked |
| $ git ls-files -t |
| H tracked |
| S tracked-but-maybe-skipped |
| $ git status --porcelain |
| $ git checkout -- '*skipped' |
| error: pathspec '*skipped' did not match any file(s) known to git |
| $ git ls-files -- '*skipped' |
| tracked-but-maybe-skipped |
| $ git checkout HEAD -- '*skipped' |
| error: pathspec '*skipped' did not match any file(s) known to git |
| $ git ls-tree HEAD | grep skipped |
| 100644 blob 276f5a64354b791b13840f02047738c77ad0584f tracked-but-maybe-skipped |
| $ git status --porcelain |
| $ git checkout HEAD~1 -- '*skipped' |
| $ git ls-files -t |
| H tracked |
| H tracked-but-maybe-skipped |
| $ git status --porcelain |
| M tracked-but-maybe-skipped |
| $ git checkout HEAD -- '*skipped' |
| $ git status --porcelain |
| $</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Note that checkout without a revision (or restore --staged) fails to |
| find a file to restore from the index, even though ls-files shows |
| such a file certainly exists.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Similar issues occur with HEAD (--source=HEAD in restore's case), |
| but suddenly works when HEAD~1 is specified. And then after that it |
| will work with HEAD specified, even though it didn't before.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>Directories are also an issue:</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>$ git sparse-checkout set nomatches |
| $ git status |
| On branch main |
| You are in a sparse checkout with 0% of tracked files present.</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>nothing to commit, working tree clean |
| $ git checkout . |
| error: pathspec '.' did not match any file(s) known to git |
| $ git checkout HEAD~1 . |
| Updated 1 path from 58916d9 |
| $ git ls-files -t |
| S tracked |
| H tracked-but-maybe-skipped</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>checkout and restore --staged, continued:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>These commands do not correctly scope operations to the sparse |
| specification, and make it worse by not setting important SKIP_WORKTREE |
| bits:</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>$ git restore --source OLDREV --staged outside-sparse-cone/ |
| $ git status --porcelain |
| MD outside-sparse-cone/file1 |
| MD outside-sparse-cone/file2 |
| MD outside-sparse-cone/file3</pre> |
| </div> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>We can add a --scope=all mode to `git restore` to let it operate outside |
| the sparse specification, but then it will be important to set the |
| SKIP_WORKTREE bits appropriately.</pre> |
| </div> |
| </div> |
| </li> |
| <li> |
| <p>Performance issues; see:</p> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>https://lore.kernel.org/git/CABPp-BEkJQoKZsQGCYioyga_uoDQ6iBeW+FKr8JhyuuTMK1RDw@mail.gmail.com/</pre> |
| </div> |
| </div> |
| </li> |
| </ol> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_reference_emails">Reference Emails</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Emails that detail various bugs we’ve had in sparse-checkout:</p> |
| </div> |
| <div class="paragraph"> |
| <p>[1] (Original descriptions of behavior A & behavior B):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/CABPp-BGJ_Nvi5TmgriD9Bh6eNXE2EDq2f8e8QKXAeYG3BxZafA@mail.gmail.com/" class="bare">https://lore.kernel.org/git/CABPp-BGJ_Nvi5TmgriD9Bh6eNXE2EDq2f8e8QKXAeYG3BxZafA@mail.gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[2] (Fix stash applications in sparse checkouts; bugs from behavioral differences):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/ccfedc7140dbf63ba26a15f93bd3885180b26517.1606861519.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/ccfedc7140dbf63ba26a15f93bd3885180b26517.1606861519.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[3] (Present-despite-skipped entries):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/11d46a399d26c913787b704d2b7169cafc28d639.1642175983.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/11d46a399d26c913787b704d2b7169cafc28d639.1642175983.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[4] (Clone --no-checkout interaction):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/pull.801.v2.git.git.1591324899170.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/pull.801.v2.git.git.1591324899170.gitgitgadget@gmail.com/</a> (clone --no-checkout)</p> |
| </div> |
| <div class="paragraph"> |
| <p>[5] (The need for update_sparsity() and avoiding <code>read-tree</code> <code>-mu</code> <code>HEAD</code>):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/3a1f084641eb47515b5a41ed4409a36128913309.1585270142.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/3a1f084641eb47515b5a41ed4409a36128913309.1585270142.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[6] (SKIP_WORKTREE is advisory, not mandatory):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/844306c3e86ef67591cc086decb2b760e7d710a3.1585270142.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/844306c3e86ef67591cc086decb2b760e7d710a3.1585270142.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[7] (<code>worktree</code> <code>add</code> should copy sparsity settings from current worktree):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/c51cb3714e7b1d2f8c9370fe87eca9984ff4859f.1644269584.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/c51cb3714e7b1d2f8c9370fe87eca9984ff4859f.1644269584.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[8] (Avoid negative surprises in add, rm, and mv):</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://lore.kernel.org/git/cover.1617914011.git.matheus.bernardino@usp.br/" class="bare">https://lore.kernel.org/git/cover.1617914011.git.matheus.bernardino@usp.br/</a></p> |
| </li> |
| <li> |
| <p><a href="https://lore.kernel.org/git/pull.1018.v4.git.1632497954.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/pull.1018.v4.git.1632497954.gitgitgadget@gmail.com/</a></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>[9] (Move from out-of-cone to in-cone):</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://lore.kernel.org/git/20220630023737.473690-6-shaoxuan.yuan02@gmail.com/" class="bare">https://lore.kernel.org/git/20220630023737.473690-6-shaoxuan.yuan02@gmail.com/</a></p> |
| </li> |
| <li> |
| <p><a href="https://lore.kernel.org/git/20220630023737.473690-4-shaoxuan.yuan02@gmail.com/" class="bare">https://lore.kernel.org/git/20220630023737.473690-4-shaoxuan.yuan02@gmail.com/</a></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>[10] (Unnecessarily downloading objects outside sparse specification):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/CAOLTT8QfwOi9yx_qZZgyGa8iL8kHWutEED7ok_jxwTcYT_hf9Q@mail.gmail.com/" class="bare">https://lore.kernel.org/git/CAOLTT8QfwOi9yx_qZZgyGa8iL8kHWutEED7ok_jxwTcYT_hf9Q@mail.gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[11] (Stolee’s comments on high-level usecases):</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/1a1e33f6-3514-9afc-0a28-5a6b85bd8014@gmail.com/" class="bare">https://lore.kernel.org/git/1a1e33f6-3514-9afc-0a28-5a6b85bd8014@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[12] Others commenting on eventually switching default to behavior A:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://lore.kernel.org/git/xmqqh719pcoo.fsf@gitster.g/" class="bare">https://lore.kernel.org/git/xmqqh719pcoo.fsf@gitster.g/</a></p> |
| </li> |
| <li> |
| <p><a href="https://lore.kernel.org/git/xmqqzgeqw0sy.fsf@gitster.g/" class="bare">https://lore.kernel.org/git/xmqqzgeqw0sy.fsf@gitster.g/</a></p> |
| </li> |
| <li> |
| <p><a href="https://lore.kernel.org/git/a86af661-cf58-a4e5-0214-a67d3a794d7e@github.com/" class="bare">https://lore.kernel.org/git/a86af661-cf58-a4e5-0214-a67d3a794d7e@github.com/</a></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>[13] Previous config name suggestion and description:</p> |
| </div> |
| <div class="literalblock"> |
| <div class="content"> |
| <pre>https://lore.kernel.org/git/CABPp-BE6zW0nJSStcVU=_DoDBnPgLqOR8pkTXK3dW11=T01OhA@mail.gmail.com/</pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>[14] Tangential issue: switch to cone mode as default sparse specification mechanism:</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/a1b68fd6126eb341ef3637bb93fedad4309b36d0.1650594746.git.gitgitgadget@gmail.com/" class="bare">https://lore.kernel.org/git/a1b68fd6126eb341ef3637bb93fedad4309b36d0.1650594746.git.gitgitgadget@gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[15] Lengthy email on grep behavior, covering what should be searched:</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/CABPp-BGVO3QdbfE84uF_3QDF0-y2iHHh6G5FAFzNRfeRitkuHw@mail.gmail.com/" class="bare">https://lore.kernel.org/git/CABPp-BGVO3QdbfE84uF_3QDF0-y2iHHh6G5FAFzNRfeRitkuHw@mail.gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[16] Email explaining sparsity patterns vs. SKIP_WORKTREE and history operations, |
| search for the parenthetical comment starting "We do not check".</p> |
| </div> |
| <div class="paragraph"> |
| <p><a href="https://lore.kernel.org/git/CABPp-BFsCPPNOZ92JQRJeGyNd0e-TCW-LcLyr0i_+VSQJP+GCg@mail.gmail.com/" class="bare">https://lore.kernel.org/git/CABPp-BFsCPPNOZ92JQRJeGyNd0e-TCW-LcLyr0i_+VSQJP+GCg@mail.gmail.com/</a></p> |
| </div> |
| <div class="paragraph"> |
| <p>[17] <a href="https://lore.kernel.org/git/20220207190320.2960362-1-jonathantanmy@google.com/" class="bare">https://lore.kernel.org/git/20220207190320.2960362-1-jonathantanmy@google.com/</a></p> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div id="footer"> |
| <div id="footer-text"> |
| Last updated 2025-10-24 14:37:25 -0700 |
| </div> |
| </div> |
| </body> |
| </html> |