Linux evo.fastest-server.com 5.14.0-284.1101.el9.tuxcare.11.els11.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Aug 14 13:30:35 UTC 2026 x86_64
LiteSpeed
Server IP : 103.249.112.113 & Your IP : 216.73.217.135
Domains : 988 Domain
User : tanishks
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
usr /
share /
doc /
git /
technical /
Delete
Unzip
Name
Size
Permission
Date
Action
api-error-handling.adoc
3.73
KB
-rw-r--r--
2025-11-17 16:38
api-error-handling.html
35.21
KB
-rw-r--r--
2026-04-05 23:58
api-index-skel.adoc
432
B
-rw-r--r--
2025-11-17 16:38
api-index.adoc
689
B
-rw-r--r--
2026-04-05 23:58
api-index.html
30.43
KB
-rw-r--r--
2026-04-05 23:59
api-index.sh
714
B
-rw-r--r--
2025-11-17 16:38
api-merge.adoc
1.06
KB
-rw-r--r--
2025-11-17 16:38
api-merge.html
31.51
KB
-rw-r--r--
2026-04-05 23:58
api-parse-options.adoc
13.01
KB
-rw-r--r--
2025-11-17 16:38
api-parse-options.html
49.44
KB
-rw-r--r--
2026-04-05 23:58
api-path-walk.adoc
3.27
KB
-rw-r--r--
2025-11-17 16:38
api-path-walk.html
34.28
KB
-rw-r--r--
2026-04-05 23:58
api-simple-ipc.adoc
4.74
KB
-rw-r--r--
2025-11-17 16:38
api-simple-ipc.html
35.49
KB
-rw-r--r--
2026-04-05 23:58
api-trace2.adoc
43.45
KB
-rw-r--r--
2025-11-17 16:38
api-trace2.html
86.12
KB
-rw-r--r--
2026-04-05 23:58
bitmap-format.adoc
14.66
KB
-rw-r--r--
2025-11-17 16:38
bitmap-format.html
49.31
KB
-rw-r--r--
2026-04-05 23:58
build-systems.adoc
7.96
KB
-rw-r--r--
2025-11-17 16:38
build-systems.html
41.02
KB
-rw-r--r--
2026-04-05 23:58
bundle-uri.adoc
26.12
KB
-rw-r--r--
2025-11-17 16:38
bundle-uri.html
63.08
KB
-rw-r--r--
2026-04-05 23:58
commit-graph.adoc
17.7
KB
-rw-r--r--
2025-11-17 16:38
commit-graph.html
52.61
KB
-rw-r--r--
2026-04-05 23:58
directory-rename-detection.adoc
5.08
KB
-rw-r--r--
2025-11-17 16:38
directory-rename-detection.html
36.33
KB
-rw-r--r--
2026-04-05 23:58
hash-function-transition.adoc
35.45
KB
-rw-r--r--
2025-11-17 16:38
hash-function-transition.html
75.42
KB
-rw-r--r--
2026-04-05 23:58
large-object-promisors.adoc
26.59
KB
-rw-r--r--
2025-11-17 16:38
large-object-promisors.html
62.72
KB
-rw-r--r--
2026-04-05 23:58
long-running-process-protocol.adoc
1.88
KB
-rw-r--r--
2025-11-17 16:38
long-running-process-protocol.html
32.18
KB
-rw-r--r--
2026-04-05 23:58
meson.build
1.49
KB
-rw-r--r--
2025-11-17 16:38
multi-pack-index.adoc
11.31
KB
-rw-r--r--
2025-11-17 16:38
multi-pack-index.html
44.62
KB
-rw-r--r--
2026-04-05 23:58
pack-heuristics.adoc
17.62
KB
-rw-r--r--
2025-11-17 16:38
pack-heuristics.html
54.83
KB
-rw-r--r--
2026-04-05 23:58
packfile-uri.adoc
3.66
KB
-rw-r--r--
2025-11-17 16:38
packfile-uri.html
34.64
KB
-rw-r--r--
2026-04-05 23:58
parallel-checkout.adoc
12.01
KB
-rw-r--r--
2025-11-17 16:38
parallel-checkout.html
44.27
KB
-rw-r--r--
2026-04-05 23:58
partial-clone.adoc
14.59
KB
-rw-r--r--
2025-11-17 16:38
partial-clone.html
48.96
KB
-rw-r--r--
2026-04-05 23:58
platform-support.adoc
8.95
KB
-rw-r--r--
2025-11-17 16:38
platform-support.html
40.71
KB
-rw-r--r--
2026-04-05 23:58
racy-git.adoc
8.91
KB
-rw-r--r--
2025-11-17 16:38
racy-git.html
41.04
KB
-rw-r--r--
2026-04-05 23:58
reftable.adoc
36.65
KB
-rw-r--r--
2025-11-17 16:38
reftable.html
86.46
KB
-rw-r--r--
2026-04-05 23:58
remembering-renames.adoc
29.71
KB
-rw-r--r--
2025-11-17 16:38
remembering-renames.html
65.78
KB
-rw-r--r--
2026-04-05 23:58
repository-version.adoc
3.19
KB
-rw-r--r--
2025-11-17 16:38
repository-version.html
33.92
KB
-rw-r--r--
2026-04-05 23:58
rerere.adoc
6.36
KB
-rw-r--r--
2025-11-17 16:38
rerere.html
38.33
KB
-rw-r--r--
2026-04-05 23:58
scalar.adoc
2.87
KB
-rw-r--r--
2025-11-17 16:38
scalar.html
33.61
KB
-rw-r--r--
2026-04-05 23:58
send-pack-pipeline.adoc
1.92
KB
-rw-r--r--
2025-11-17 16:38
send-pack-pipeline.html
32.41
KB
-rw-r--r--
2026-04-05 23:58
shallow.adoc
2.49
KB
-rw-r--r--
2025-11-17 16:38
shallow.html
32.77
KB
-rw-r--r--
2026-04-05 23:58
sparse-checkout.adoc
46.68
KB
-rw-r--r--
2025-11-17 16:38
sparse-checkout.html
92.36
KB
-rw-r--r--
2026-04-05 23:58
sparse-index.adoc
9.28
KB
-rw-r--r--
2025-11-17 16:38
sparse-index.html
41.79
KB
-rw-r--r--
2026-04-05 23:58
trivial-merge.adoc
4.16
KB
-rw-r--r--
2025-11-17 16:38
trivial-merge.html
35.33
KB
-rw-r--r--
2026-04-05 23:58
unit-tests.adoc
9.8
KB
-rw-r--r--
2025-11-17 16:38
unit-tests.html
50.73
KB
-rw-r--r--
2026-04-05 23:58
Save
Rename
<!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.12"/> <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 @import statement to use as 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";*/ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block} audio,video{display:inline-block} audio:not([controls]){display:none;height:0} html{font-family:sans-serif;-ms-text-size-adjust:100%;-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} abbr[title]{border-bottom:1px dotted} b,strong{font-weight:bold} dfn{font-style:italic} hr{-moz-box-sizing:content-box;box-sizing:content-box;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} 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"]{box-sizing:border-box;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{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;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;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;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{font-family:inherit;font-weight:400;font-size:1em;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;height:0} 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{font-size:1em;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;font-size:1em} ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} ul.square{list-style-type:square} ul.circle{list-style-type:circle} ul.disc{list-style-type:disc} 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} abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} abbr{text-transform:none} blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} blockquote cite::before{content:"\2014 \0020"} blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} 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:solid 1px #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;-webkit-border-radius:4px;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;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;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-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;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,body.toc2 #header>h1:nth-last-child(2){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:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;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} #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-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;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:rgba(255,255,255,.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>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em} .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)} table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit} .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-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} .exampleblock>.content>:first-child{margin-top:0} .exampleblock>.content>:last-child{margin-bottom:0} .sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px} .sidebarblock>:first-child{margin-top:0} .sidebarblock>:last-child{margin-bottom:0} .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} .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>: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{-webkit-border-radius:4px;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;-webkit-border-radius:4px;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{border-right:1px solid currentColor;opacity:.35;padding-right:.5em} pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em} pre.pygments .lineno::before{content:"";margin-right:-.125em} .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;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;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} ol>li p,ul>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} ul.checklist{margin-left:.625em} 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:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;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:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;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{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} .gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} .gist .file-data>table td.line-data{width:99%} 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);-webkit-border-radius:50%;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{letter-spacing:-.01em} p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} p,blockquote,dt,td.content,span.alt{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;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} .print-only{display:none!important} @page{margin:1.25cm .75cm} @media print{*{-webkit-box-shadow:none!important;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]::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 print,amzn-kf8{#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-11-17 16:38:17 UTC </div> </div> </body> </html>