2 <html xmlns=
"http://www.w3.org/1999/xhtml" lang=
"en">
4 <meta charset=
"UTF-8"/>
5 <meta http-equiv=
"X-UA-Compatible" content=
"IE=edge"/>
6 <meta name=
"viewport" content=
"width=device-width, initial-scale=1.0"/>
7 <meta name=
"generator" content=
"Asciidoctor 2.0.20"/>
8 <title>Partial Clone Design Notes
</title>
9 <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"/>
11 /*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
12 /* Uncomment the following line when using as a custom stylesheet */
13 /* @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"; */
14 html{font-family:sans-serif;-webkit-text-size-adjust:
100%}
16 a:focus{outline:thin dotted}
17 a:active,a:hover{outline:
0}
18 h1{font-size:
2em;margin:
.67em
0}
19 b,strong{font-weight:bold}
21 abbr[title]{cursor:help;border-bottom:
1px dotted #dddddf;text-decoration:none}
22 dfn{font-style:italic}
24 mark{background:#ff0;color:#
000}
25 code,kbd,pre,samp{font-family:monospace;font-size:
1em}
26 pre{white-space:pre-wrap}
27 q{quotes:
"\201C" "\201D" "\2018" "\2019"}
29 sub,sup{font-size:
75%;line-height:
0;position:relative;vertical-align:baseline}
33 svg:not(:root){overflow:hidden}
35 audio,video{display:inline-block}
36 audio:not([controls]){display:none;height:
0}
37 fieldset{border:
1px solid silver;margin:
0 2px;padding:
.35em
.625em
.75em}
38 legend{border:
0;padding:
0}
39 button,input,select,textarea{font-family:inherit;font-size:
100%;margin:
0}
40 button,input{line-height:normal}
41 button,select{text-transform:none}
42 button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
43 button[disabled],html input[disabled]{cursor:default}
44 input[type=checkbox],input[type=radio]{padding:
0}
45 button::-moz-focus-inner,input::-moz-focus-inner{border:
0;padding:
0}
46 textarea{overflow:auto;vertical-align:top}
47 table{border-collapse:collapse;border-spacing:
0}
48 *,::before,::after{box-sizing:border-box}
49 html,body{font-size:
100%}
50 body{background:#fff;color:rgba(
0,
0,
0,
.8);padding:
0;margin:
0;font-family:
"Noto Serif",
"DejaVu Serif",serif;line-height:
1;position:relative;cursor:auto;-moz-tab-size:
4;-o-tab-size:
4;tab-size:
4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
51 a:hover{cursor:pointer}
52 img,object,embed{max-width:
100%;height:auto}
53 object,embed{height:
100%}
54 img{-ms-interpolation-mode:bicubic}
55 .left{float:left!important}
56 .right{float:right!important}
57 .text-left{text-align:left!important}
58 .text-right{text-align:right!important}
59 .text-center{text-align:center!important}
60 .text-justify{text-align:justify!important}
62 img,object,svg{display:inline-block;vertical-align:middle}
63 textarea{height:auto;min-height:
50px}
65 .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}
66 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}
67 a{color:#
2156a5;text-decoration:underline;line-height:inherit}
68 a:hover,a:focus{color:#
1d4b8f}
70 p{line-height:
1.6;margin-bottom:
1.25em;text-rendering:optimizeLegibility}
71 p aside{font-size:
.875em;line-height:
1.35;font-style:italic}
72 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}
73 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}
75 h2{font-size:
1.6875em}
76 h3,#toctitle,.sidebarblock
>.content
>.title{font-size:
1.375em}
77 h4,h5{font-size:
1.125em}
79 hr{border:solid #dddddf;border-width:
1px
0 0;clear:both;margin:
1.25em
0 1.1875em}
80 em,i{font-style:italic;line-height:inherit}
81 strong,b{font-weight:bold;line-height:inherit}
82 small{font-size:
60%;line-height:inherit}
83 code{font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;font-weight:
400;color:rgba(
0,
0,
0,
.9)}
84 ul,ol,dl{line-height:
1.6;margin-bottom:
1.25em;list-style-position:outside;font-family:inherit}
85 ul,ol{margin-left:
1.5em}
86 ul li ul,ul li ol{margin-left:
1.25em;margin-bottom:
0}
87 ul.circle{list-style-type:circle}
88 ul.disc{list-style-type:disc}
89 ul.square{list-style-type:square}
90 ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
91 ol li ul,ol li ol{margin-left:
1.25em;margin-bottom:
0}
92 dl dt{margin-bottom:
.3125em;font-weight:bold}
93 dl dd{margin-bottom:
1.25em}
94 blockquote{margin:
0 0 1.25em;padding:
.5625em
1.25em
0 1.1875em;border-left:
1px solid #ddd}
95 blockquote,blockquote p{line-height:
1.6;color:rgba(
0,
0,
0,
.85)}
96 @media screen and (min-width:
768px){h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6{line-height:
1.2}
98 h2{font-size:
2.3125em}
99 h3,#toctitle,.sidebarblock
>.content
>.title{font-size:
1.6875em}
100 h4{font-size:
1.4375em}}
101 table{background:#fff;margin-bottom:
1.25em;border:
1px solid #dedede;word-wrap:normal}
102 table thead,table tfoot{background:#f7f8f7}
103 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}
104 table tr th,table tr td{padding:
.5625em
.625em;font-size:inherit;color:rgba(
0,
0,
0,
.8)}
105 table tr.even,table tr.alt{background:#f8f8f7}
106 table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:
1.6}
107 h1,h2,h3,#toctitle,.sidebarblock
>.content
>.title,h4,h5,h6{line-height:
1.2;word-spacing:-
.05em}
108 h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock
>.content
>.title strong,h4 strong,h5 strong,h6 strong{font-weight:
400}
109 .center{margin-left:auto;margin-right:auto}
111 .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:
" ";display:table}
112 .clearfix::after,.float-group::after{clear:both}
113 :not(pre).nobreak{word-wrap:normal}
114 :not(pre).nowrap{white-space:nowrap}
115 :not(pre).pre-wrap{white-space:pre-wrap}
116 :not(pre):not([class^=L])
>code{font-size:
.9375em;font-style:normal!important;letter-spacing:
0;padding:
.1em
.5ex;word-spacing:-
.15em;background:#f7f7f8;border-radius:
4px;line-height:
1.45;text-rendering:optimizeSpeed}
117 pre{color:rgba(
0,
0,
0,
.9);font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;line-height:
1.45;text-rendering:optimizeSpeed}
118 pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
119 pre
>code{display:block}
120 pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
121 em em{font-style:normal}
122 strong strong{font-weight:
400}
123 .keyseq{color:rgba(
51,
51,
51,
.8)}
124 kbd{font-family:
"Droid Sans Mono",
"DejaVu Sans Mono",monospace;display:inline-block;color:rgba(
0,
0,
0,
.8);font-size:
.65em;line-height:
1.45;background:#f7f7f7;border:
1px solid #ccc;border-radius:
3px;box-shadow:
0 1px
0 rgba(
0,
0,
0,
.2),inset
0 0 0 .1em #fff;margin:
0 .15em;padding:
.2em
.5em;vertical-align:middle;position:relative;top:-
.1em;white-space:nowrap}
125 .keyseq kbd:first-child{margin-left:
0}
126 .keyseq kbd:last-child{margin-right:
0}
127 .menuseq,.menuref{color:#
000}
128 .menuseq b:not(.caret),.menuref{font-weight:inherit}
129 .menuseq{word-spacing:-
.02em}
130 .menuseq b.caret{font-size:
1.25em;line-height:
.8}
131 .menuseq i.caret{font-weight:bold;text-align:center;width:
.45em}
132 b.button::before,b.button::after{position:relative;top:-
1px;font-weight:
400}
133 b.button::before{content:
"[";padding:
0 3px
0 2px}
134 b.button::after{content:
"]";padding:
0 2px
0 3px}
135 p a
>code:hover{color:rgba(
0,
0,
0,
.9)}
136 #header,#content,#footnotes,#footer{width:
100%;margin:
0 auto;max-width:
62.5em;*zoom:
1;position:relative;padding-left:
.9375em;padding-right:
.9375em}
137 #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:
" ";display:table}
138 #header::after,#content::after,#footnotes::after,#footer::after{clear:both}
139 #content{margin-top:
1.25em}
140 #content::before{content:none}
141 #header
>h1:first-child{color:rgba(
0,
0,
0,
.85);margin-top:
2.25rem;margin-bottom:
0}
142 #header
>h1:first-child+#toc{margin-top:
8px;border-top:
1px solid #dddddf}
143 #header
>h1:only-child,body.toc2 #header
>h1:nth-last-child(
2){border-bottom:
1px solid #dddddf;padding-bottom:
8px}
144 #header .details{border-bottom:
1px solid #dddddf;line-height:
1.45;padding-top:
.25em;padding-bottom:
.25em;padding-left:
.25em;color:rgba(
0,
0,
0,
.6);display:flex;flex-flow:row wrap}
145 #header .details span:first-child{margin-left:-
.125em}
146 #header .details span.email a{color:rgba(
0,
0,
0,
.85)}
147 #header .details br{display:none}
148 #header .details br+span::before{content:
"\00a0\2013\00a0"}
149 #header .details br+span.author::before{content:
"\00a0\22c5\00a0";color:rgba(
0,
0,
0,
.85)}
150 #header .details br+span#revremark::before{content:
"\00a0|\00a0"}
151 #header #revnumber{text-transform:capitalize}
152 #header #revnumber::after{content:
"\00a0"}
153 #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}
154 #toc{border-bottom:
1px solid #e7e7e9;padding-bottom:
.5em}
155 #toc
>ul{margin-left:
.125em}
156 #toc ul.sectlevel0
>li
>a{font-style:italic}
157 #toc ul.sectlevel0 ul.sectlevel1{margin:
.5em
0}
158 #toc ul{font-family:
"Open Sans",
"DejaVu Sans",sans-serif;list-style-type:none}
159 #toc li{line-height:
1.3334;margin-top:
.3334em}
160 #toc a{text-decoration:none}
161 #toc a:active{text-decoration:underline}
162 #toctitle{color:#
7a2518;font-size:
1.2em}
163 @media screen and (min-width:
768px){#toctitle{font-size:
1.375em}
164 body.toc2{padding-left:
15em;padding-right:
0}
165 #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}
166 #toc.toc2 #toctitle{margin-top:
0;margin-bottom:
.8rem;font-size:
1.2em}
167 #toc.toc2
>ul{font-size:
.9em;margin-bottom:
0}
168 #toc.toc2 ul ul{margin-left:
0;padding-left:
1em}
169 #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:
0;margin-top:
.5em;margin-bottom:
.5em}
170 body.toc2.toc-right{padding-left:
0;padding-right:
15em}
171 body.toc2.toc-right #toc.toc2{border-right-width:
0;border-left:
1px solid #e7e7e9;left:auto;right:
0}}
172 @media screen and (min-width:
1280px){body.toc2{padding-left:
20em;padding-right:
0}
173 #toc.toc2{width:
20em}
174 #toc.toc2 #toctitle{font-size:
1.375em}
175 #toc.toc2
>ul{font-size:
.95em}
176 #toc.toc2 ul ul{padding-left:
1.25em}
177 body.toc2.toc-right{padding-left:
0;padding-right:
20em}}
178 #content #toc{border:
1px solid #e0e0dc;margin-bottom:
1.25em;padding:
1.25em;background:#f8f8f7;border-radius:
4px}
179 #content #toc
>:first-child{margin-top:
0}
180 #content #toc
>:last-child{margin-bottom:
0}
181 #footer{max-width:none;background:rgba(
0,
0,
0,
.8);padding:
1.25em}
182 #footer-text{color:hsla(
0,
0%,
100%,
.8);line-height:
1.44}
183 #content{margin-bottom:
.625em}
184 .sect1{padding-bottom:
.625em}
185 @media screen and (min-width:
768px){#content{margin-bottom:
1.25em}
186 .sect1{padding-bottom:
1.25em}}
187 .sect1:last-child{padding-bottom:
0}
188 .sect1+.sect1{border-top:
1px solid #e7e7e9}
189 #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}
190 #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}
191 #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}
192 #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}
193 #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}
194 details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:
1.25em}
195 details{margin-left:
1.25rem}
196 details
>summary{cursor:pointer;display:block;position:relative;line-height:
1.6;margin-bottom:
.625rem;outline:none;-webkit-tap-highlight-color:transparent}
197 details
>summary::-webkit-details-marker{display:none}
198 details
>summary::before{content:
"";border:solid transparent;border-left:solid;border-width:
.3em
0 .3em
.5em;position:absolute;top:
.5em;left:-
1.25rem;transform:translateX(
15%)}
199 details[open]
>summary::before{border:solid transparent;border-top:solid;border-width:
.5em
.3em
0;transform:translateY(
15%)}
200 details
>summary::after{content:
"";width:
1.25rem;height:
1em;position:absolute;top:
.3em;left:-
1.25rem}
201 .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}
202 table.tableblock.fit-content
>caption.title{white-space:nowrap;width:
0}
203 .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)}
204 .admonitionblock
>table{border-collapse:separate;border:
0;background:none;width:
100%}
205 .admonitionblock
>table td.icon{text-align:center;width:
80px}
206 .admonitionblock
>table td.icon img{max-width:none}
207 .admonitionblock
>table td.icon .title{font-weight:bold;font-family:
"Open Sans",
"DejaVu Sans",sans-serif;text-transform:uppercase}
208 .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}
209 .admonitionblock
>table td.content
>:last-child
>:last-child{margin-bottom:
0}
210 .exampleblock
>.content{border:
1px solid #e6e6e6;margin-bottom:
1.25em;padding:
1.25em;background:#fff;border-radius:
4px}
211 .sidebarblock{border:
1px solid #dbdbd6;margin-bottom:
1.25em;padding:
1.25em;background:#f3f3f2;border-radius:
4px}
212 .sidebarblock
>.content
>.title{color:#
7a2518;margin-top:
0;text-align:center}
213 .exampleblock
>.content
>:first-child,.sidebarblock
>.content
>:first-child{margin-top:
0}
214 .exampleblock
>.content
>:last-child,.exampleblock
>.content
>:last-child
>:last-child,.exampleblock
>.content .olist
>ol
>li:last-child
>:last-child,.exampleblock
>.content .ulist
>ul
>li:last-child
>:last-child,.exampleblock
>.content .qlist
>ol
>li:last-child
>:last-child,.sidebarblock
>.content
>:last-child,.sidebarblock
>.content
>:last-child
>:last-child,.sidebarblock
>.content .olist
>ol
>li:last-child
>:last-child,.sidebarblock
>.content .ulist
>ul
>li:last-child
>:last-child,.sidebarblock
>.content .qlist
>ol
>li:last-child
>:last-child{margin-bottom:
0}
215 .literalblock pre,.listingblock
>.content
>pre{border-radius:
4px;overflow-x:auto;padding:
1em;font-size:
.8125em}
216 @media screen and (min-width:
768px){.literalblock pre,.listingblock
>.content
>pre{font-size:
.90625em}}
217 @media screen and (min-width:
1280px){.literalblock pre,.listingblock
>.content
>pre{font-size:
1em}}
218 .literalblock pre,.listingblock
>.content
>pre:not(.highlight),.listingblock
>.content
>pre[class=highlight],.listingblock
>.content
>pre[class^=
"highlight "]{background:#f7f7f8}
219 .literalblock.output pre{color:#f7f7f8;background:rgba(
0,
0,
0,
.9)}
220 .listingblock
>.content{position:relative}
221 .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}
222 .listingblock:hover code[data-lang]::before{display:block}
223 .listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:
.5em;color:inherit;opacity:
.5}
224 .listingblock.terminal pre .command:not([data-prompt])::before{content:
"$"}
225 .listingblock pre.highlightjs{padding:
0}
226 .listingblock pre.highlightjs
>code{padding:
1em;border-radius:
4px}
227 .listingblock pre.prettyprint{border-width:
0}
228 .prettyprint{background:#f7f7f8}
229 pre.prettyprint .linenums{line-height:
1.45;margin-left:
2em}
230 pre.prettyprint li{background:none;list-style-type:inherit;padding-left:
0}
231 pre.prettyprint li code[data-lang]::before{opacity:
1}
232 pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
233 table.linenotable{border-collapse:separate;border:
0;margin-bottom:
0;background:none}
234 table.linenotable td[class]{color:inherit;vertical-align:top;padding:
0;line-height:inherit;white-space:normal}
235 table.linenotable td.code{padding-left:
.75em}
236 table.linenotable td.linenos,pre.pygments .linenos{border-right:
1px solid;opacity:
.35;padding-right:
.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
237 pre.pygments span.linenos{display:inline-block;margin-right:
.75em}
238 .quoteblock{margin:
0 1em
1.25em
1.5em;display:table}
239 .quoteblock:not(.excerpt)
>.title{margin-left:-
1.5em;margin-bottom:
.75em}
240 .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}
241 .quoteblock blockquote{margin:
0;padding:
0;border:
0}
242 .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)}
243 .quoteblock blockquote
>.paragraph:last-child p{margin-bottom:
0}
244 .quoteblock .attribution{margin-top:
.75em;margin-right:
.5ex;text-align:right}
245 .verseblock{margin:
0 1em
1.25em}
246 .verseblock pre{font-family:
"Open Sans",
"DejaVu Sans",sans-serif;font-size:
1.15rem;color:rgba(
0,
0,
0,
.85);font-weight:
300;text-rendering:optimizeLegibility}
247 .verseblock pre strong{font-weight:
400}
248 .verseblock .attribution{margin-top:
1.25rem;margin-left:
.5ex}
249 .quoteblock .attribution,.verseblock .attribution{font-size:
.9375em;line-height:
1.45;font-style:italic}
250 .quoteblock .attribution br,.verseblock .attribution br{display:none}
251 .quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-
.025em;color:rgba(
0,
0,
0,
.6)}
252 .quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
253 .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}
254 .quoteblock.abstract{margin:
0 1em
1.25em;display:block}
255 .quoteblock.abstract
>.title{margin:
0 0 .375em;font-size:
1.15em;text-align:center}
256 .quoteblock.excerpt
>blockquote,.quoteblock .quoteblock{padding:
0 0 .25em
1em;border-left:
.25em solid #dddddf}
257 .quoteblock.excerpt,.quoteblock .quoteblock{margin-left:
0}
258 .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:
1.0625rem}
259 .quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:
.85rem;text-align:left;margin-right:
0}
260 p.tableblock:last-child{margin-bottom:
0}
261 td.tableblock
>.content{margin-bottom:
1.25em;word-wrap:anywhere}
262 td.tableblock
>.content
>:last-child{margin-bottom:-
1.25em}
263 table.tableblock,th.tableblock,td.tableblock{border:
0 solid #dedede}
264 table.grid-all
>*
>tr
>*{border-width:
1px}
265 table.grid-cols
>*
>tr
>*{border-width:
0 1px}
266 table.grid-rows
>*
>tr
>*{border-width:
1px
0}
267 table.frame-all{border-width:
1px}
268 table.frame-ends{border-width:
1px
0}
269 table.frame-sides{border-width:
0 1px}
270 table.frame-none
>colgroup+*
>:first-child
>*,table.frame-sides
>colgroup+*
>:first-child
>*{border-top-width:
0}
271 table.frame-none
>:last-child
>:last-child
>*,table.frame-sides
>:last-child
>:last-child
>*{border-bottom-width:
0}
272 table.frame-none
>*
>tr
>:first-child,table.frame-ends
>*
>tr
>:first-child{border-left-width:
0}
273 table.frame-none
>*
>tr
>:last-child,table.frame-ends
>*
>tr
>:last-child{border-right-width:
0}
274 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}
275 th.halign-left,td.halign-left{text-align:left}
276 th.halign-right,td.halign-right{text-align:right}
277 th.halign-center,td.halign-center{text-align:center}
278 th.valign-top,td.valign-top{vertical-align:top}
279 th.valign-bottom,td.valign-bottom{vertical-align:bottom}
280 th.valign-middle,td.valign-middle{vertical-align:middle}
281 table thead th,table tfoot th{font-weight:bold}
282 tbody tr th{background:#f7f8f7}
283 tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(
0,
0,
0,
.8);font-weight:bold}
284 p.tableblock
>code:only-child{background:none;padding:
0}
285 p.tableblock{font-size:
1em}
286 ol{margin-left:
1.75em}
287 ul li ol{margin-left:
1.5em}
288 dl dd{margin-left:
1.125em}
289 dl dd:last-child,dl dd:last-child
>:last-child{margin-bottom:
0}
290 li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:
.625em}
291 ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
292 ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:
.625em}
293 ul.unstyled,ol.unstyled{margin-left:
0}
294 li
>p:empty:only-child::before{content:
"";display:inline-block}
295 ul.checklist
>li
>p:first-child{margin-left:-
1em}
296 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}
297 ul.checklist
>li
>p:first-child
>input[type=checkbox]:first-child{margin-right:
.25em}
298 ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:
0 0 .625em -
1.25em}
299 ul.inline
>li{margin-left:
1.25em}
300 .unstyled dl dt{font-weight:
400;font-style:normal}
301 ol.arabic{list-style-type:decimal}
302 ol.decimal{list-style-type:decimal-leading-zero}
303 ol.loweralpha{list-style-type:lower-alpha}
304 ol.upperalpha{list-style-type:upper-alpha}
305 ol.lowerroman{list-style-type:lower-roman}
306 ol.upperroman{list-style-type:upper-roman}
307 ol.lowergreek{list-style-type:lower-greek}
308 .hdlist
>table,.colist
>table{border:
0;background:none}
309 .hdlist
>table
>tbody
>tr,.colist
>table
>tbody
>tr{background:none}
310 td.hdlist1,td.hdlist2{vertical-align:top;padding:
0 .625em}
311 td.hdlist1{font-weight:bold;padding-bottom:
1.25em}
312 td.hdlist2{word-wrap:anywhere}
313 .literalblock+.colist,.listingblock+.colist{margin-top:-
.5em}
314 .colist td:not([class]):first-child{padding:
.4em
.75em
0;line-height:
1;vertical-align:top}
315 .colist td:not([class]):first-child img{max-width:none}
316 .colist td:not([class]):last-child{padding:
.25em
0}
317 .thumb,.th{line-height:
0;display:inline-block;border:
4px solid #fff;box-shadow:
0 0 0 1px #ddd}
318 .imageblock.left{margin:
.25em
.625em
1.25em
0}
319 .imageblock.right{margin:
.25em
0 1.25em
.625em}
320 .imageblock
>.title{margin-bottom:
0}
321 .imageblock.thumb,.imageblock.th{border-width:
6px}
322 .imageblock.thumb
>.title,.imageblock.th
>.title{padding:
0 .125em}
323 .image.left,.image.right{margin-top:
.25em;margin-bottom:
.25em;display:inline-block;line-height:
0}
324 .image.left{margin-right:
.625em}
325 .image.right{margin-left:
.625em}
326 a.image{text-decoration:none;display:inline-block}
327 a.image object{pointer-events:none}
328 sup.footnote,sup.footnoteref{font-size:
.875em;position:static;vertical-align:super}
329 sup.footnote a,sup.footnoteref a{text-decoration:none}
330 sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
331 #footnotes{padding-top:
.75em;padding-bottom:
.75em;margin-bottom:
.625em}
332 #footnotes hr{width:
20%;min-width:
6.25em;margin:-
.25em
0 .75em;border-width:
1px
0 0}
333 #footnotes .footnote{padding:
0 .375em
0 .225em;line-height:
1.3334;font-size:
.875em;margin-left:
1.2em;margin-bottom:
.2em}
334 #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-
1.05em}
335 #footnotes .footnote:last-of-type{margin-bottom:
0}
336 #content #footnotes{margin-top:-
.625em;margin-bottom:
0;padding:
.75em
0}
337 div.unbreakable{page-break-inside:avoid}
338 .big{font-size:larger}
339 .small{font-size:smaller}
340 .underline{text-decoration:underline}
341 .overline{text-decoration:overline}
342 .line-through{text-decoration:line-through}
344 .aqua-background{background:#
00fafa}
346 .black-background{background:#
000}
348 .blue-background{background:#
0000fa}
349 .fuchsia{color:#bf00bf}
350 .fuchsia-background{background:#fa00fa}
352 .gray-background{background:#
7d7d7d}
353 .green{color:#
006000}
354 .green-background{background:#
007d00}
356 .lime-background{background:#
00fa00}
357 .maroon{color:#
600000}
358 .maroon-background{background:#
7d0000}
360 .navy-background{background:#
00007d}
361 .olive{color:#
606000}
362 .olive-background{background:#
7d7d00}
363 .purple{color:#
600060}
364 .purple-background{background:#
7d007d}
366 .red-background{background:#fa0000}
367 .silver{color:#
909090}
368 .silver-background{background:#bcbcbc}
370 .teal-background{background:#
007d7d}
371 .white{color:#bfbfbf}
372 .white-background{background:#fafafa}
373 .yellow{color:#bfbf00}
374 .yellow-background{background:#fafa00}
375 span.icon
>.fa{cursor:default}
376 a span.icon
>.fa{cursor:inherit}
377 .admonitionblock td.icon [class^=
"fa icon-"]{font-size:
2.5em;text-shadow:
1px
1px
2px rgba(
0,
0,
0,
.5);cursor:default}
378 .admonitionblock td.icon .icon-note::before{content:
"\f05a";color:#
19407c}
379 .admonitionblock td.icon .icon-tip::before{content:
"\f0eb";text-shadow:
1px
1px
2px rgba(
155,
155,
0,
.8);color:#
111}
380 .admonitionblock td.icon .icon-warning::before{content:
"\f071";color:#bf6900}
381 .admonitionblock td.icon .icon-caution::before{content:
"\f06d";color:#bf3400}
382 .admonitionblock td.icon .icon-important::before{content:
"\f06a";color:#bf0000}
383 .conum[data-value]{display:inline-block;color:#fff!important;background:rgba(
0,
0,
0,
.8);border-radius:
50%;text-align:center;font-size:
.75em;width:
1.67em;height:
1.67em;line-height:
1.67em;font-family:
"Open Sans",
"DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
384 .conum[data-value] *{color:#fff!important}
385 .conum[data-value]+b{display:none}
386 .conum[data-value]::after{content:attr(data-value)}
387 pre .conum[data-value]{position:relative;top:-
.125em}
388 b.conum *{color:inherit!important}
389 .conum:not([data-value]):empty{display:none}
390 dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
391 h1,h2,p,td.content,span.alt,summary{letter-spacing:-
.01em}
392 p strong,td.content strong,div.footnote strong{letter-spacing:-
.005em}
393 p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:
1.0625rem}
394 p{margin-bottom:
1.25rem}
395 .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:
1em}
396 .exampleblock
>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:
0 1px
4px #e0e0dc}
397 .print-only{display:none!important}
398 @page{margin:
1.25cm
.75cm}
399 @media print{*{box-shadow:none!important;text-shadow:none!important}
401 a{color:inherit!important;text-decoration:underline!important}
402 a.bare,a[href^=
"#"],a[href^=
"mailto:"]{text-decoration:none!important}
403 a[href^=
"http:"]:not(.bare)::after,a[href^=
"https:"]:not(.bare)::after{content:
"(" attr(href)
")";display:inline-block;font-size:
.875em;padding-left:
.25em}
404 abbr[title]{border-bottom:
1px dotted}
405 abbr[title]::after{content:
" (" attr(title)
")"}
406 pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
407 thead{display:table-header-group}
409 p,blockquote,dt,td.content{font-size:
1em;orphans:
3;widows:
3}
410 h2,h3,#toctitle,.sidebarblock
>.content
>.title{page-break-after:avoid}
411 #header,#content,#footnotes,#footer{max-width:none}
412 #toc,.sidebarblock,.exampleblock
>.content{background:none!important}
413 #toc{border-bottom:
1px solid #dddddf!important;padding-bottom:
0!important}
414 body.book #header{text-align:center}
415 body.book #header
>h1:first-child{border:
0!important;margin:
2.5em
0 1em}
416 body.book #header .details{border:
0!important;display:block;padding:
0!important}
417 body.book #header .details span:first-child{margin-left:
0!important}
418 body.book #header .details br{display:block}
419 body.book #header .details br+span::before{content:none!important}
420 body.book #toc{border:
0!important;text-align:left!important;padding:
0!important;margin:
0!important}
421 body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1
>h2{page-break-before:always}
422 .listingblock code[data-lang]::before{display:block}
423 #footer{padding:
0 .9375em}
424 .hide-on-print{display:none!important}
425 .print-only{display:block!important}
426 .hide-for-print{display:none!important}
427 .show-for-print{display:inherit!important}}
428 @media amzn-kf8,print{#header
>h1:first-child{margin-top:
1.25rem}
429 .sect1{padding:
0!important}
430 .sect1+.sect1{border:
0}
431 #footer{background:none}
432 #footer-text{color:rgba(
0,
0,
0,
.6);font-size:
.9em}}
433 @media amzn-kf8{#header,#content,#footnotes,#footer{padding:
0}}
436 <body class=
"article">
438 <h1>Partial Clone Design Notes
</h1>
439 <div class=
"details">
440 <span id=
"revdate">2024-
11-
22</span>
445 <div class=
"sectionbody">
446 <div class=
"paragraph">
447 <p>The
"Partial Clone" feature is a performance optimization for Git that
448 allows Git to function without having a complete copy of the repository.
449 The goal of this work is to allow Git to better handle extremely large
452 <div class=
"paragraph">
453 <p>During clone and fetch operations, Git downloads the complete contents
454 and history of the repository. This includes all commits, trees, and
455 blobs for the complete life of the repository. For extremely large
456 repositories, clones can take hours (or days) and consume
100+GiB of disk
459 <div class=
"paragraph">
460 <p>Often in these repositories there are many blobs and trees that the user
461 does not need such as:
</p>
463 <div class=
"olist arabic">
466 <p>files outside of the user
’s work area in the tree. For example, in
467 a repository with
500K directories and
3.5M files in every commit,
468 we can avoid downloading many objects if the user only needs a
469 narrow
"cone" of the source tree.
</p>
472 <p>large binary assets. For example, in a repository where large build
473 artifacts are checked into the tree, we can avoid downloading all
474 previous versions of these non-mergeable binary assets and only
475 download versions that are actually referenced.
</p>
479 <div class=
"paragraph">
480 <p>Partial clone allows us to avoid downloading such unneeded objects
<strong>in
481 advance
</strong> during clone and fetch operations and thereby reduce download
482 times and disk usage. Missing objects can later be
"demand fetched"
485 <div class=
"paragraph">
486 <p>A remote that can later provide the missing objects is called a
487 promisor remote, as it promises to send the objects when
488 requested. Initially Git supported only one promisor remote, the origin
489 remote from which the user cloned and that was configured in the
490 "extensions.partialClone" config option. Later support for more than
491 one promisor remote has been implemented.
</p>
493 <div class=
"paragraph">
494 <p>Use of partial clone requires that the user be online and the origin
495 remote or other promisor remotes be available for on-demand fetching
496 of missing objects. This may or may not be problematic for the user.
497 For example, if the user can stay within the pre-selected subset of
498 the source tree, they may not encounter any missing objects.
499 Alternatively, the user could try to pre-fetch various objects if they
500 know that they are going offline.
</p>
505 <h2 id=
"_non_goals">Non-Goals
</h2>
506 <div class=
"sectionbody">
507 <div class=
"paragraph">
508 <p>Partial clone is a mechanism to limit the number of blobs and trees downloaded
509 <strong>within
</strong> a given range of commits
 — and is therefore independent of and not
510 intended to conflict with existing DAG-level mechanisms to limit the set of
511 requested commits (i.e. shallow clone, single branch, or fetch
<em><refspec
></em>).
</p>
516 <h2 id=
"_design_overview">Design Overview
</h2>
517 <div class=
"sectionbody">
518 <div class=
"paragraph">
519 <p>Partial clone logically consists of the following parts:
</p>
524 <p>A mechanism for the client to describe unneeded or unwanted objects to
528 <p>A mechanism for the server to omit such unwanted objects from packfiles
529 sent to the client.
</p>
532 <p>A mechanism for the client to gracefully handle missing objects (that
533 were previously omitted by the server).
</p>
536 <p>A mechanism for the client to backfill missing objects as needed.
</p>
543 <h2 id=
"_design_details">Design Details
</h2>
544 <div class=
"sectionbody">
548 <p>A new pack-protocol capability
"filter" is added to the fetch-pack and
549 upload-pack negotiation.
</p>
550 <div class=
"paragraph">
551 <p>This uses the existing capability discovery mechanism.
552 See
"filter" in
<a href=
"../gitprotocol-pack.html">gitprotocol-pack(
5)
</a>.
</p>
556 <p>Clients pass a
"filter-spec" to clone and fetch which is passed to the
557 server to request filtering during packfile construction.
</p>
558 <div class=
"paragraph">
559 <p>There are various filters available to accommodate different situations.
560 See
"--filter=<filter-spec>" in Documentation/rev-list-options.txt.
</p>
564 <p>On the server pack-objects applies the requested filter-spec as it
565 creates
"filtered" packfiles for the client.
</p>
566 <div class=
"paragraph">
567 <p>These filtered packfiles are
<strong>incomplete
</strong> in the traditional sense because
568 they may contain objects that reference objects not contained in the
569 packfile and that the client doesn
’t already have. For example, the
570 filtered packfile may contain trees or tags that reference missing blobs
571 or commits that reference missing trees.
</p>
575 <p>On the client these incomplete packfiles are marked as
"promisor packfiles"
576 and treated differently by various commands.
</p>
579 <p>On the client a repository extension is added to the local config to
580 prevent older versions of git from failing mid-operation because of
581 missing objects that they cannot handle.
582 See
<code>extensions.partialClone
</code> in
<a href=
"../git-config.html">git-config(
1)
</a>.
</p>
589 <h2 id=
"_handling_missing_objects">Handling Missing Objects
</h2>
590 <div class=
"sectionbody">
594 <p>An object may be missing due to a partial clone or fetch, or missing
595 due to repository corruption. To differentiate these cases, the
596 local repository specially indicates such filtered packfiles
597 obtained from promisor remotes as
"promisor packfiles".
</p>
598 <div class=
"paragraph">
599 <p>These promisor packfiles consist of a
"<name>.promisor" file with
600 arbitrary contents (like the
"<name>.keep" files), in addition to
601 their
"<name>.pack" and
"<name>.idx" files.
</p>
605 <p>The local repository considers a
"promisor object" to be an object that
606 it knows (to the best of its ability) that promisor remotes have promised
607 that they have, either because the local repository has that object in one of
608 its promisor packfiles, or because another promisor object refers to it.
</p>
609 <div class=
"paragraph">
610 <p>When Git encounters a missing object, Git can see if it is a promisor object
611 and handle it appropriately. If not, Git can report a corruption.
</p>
613 <div class=
"paragraph">
614 <p>This means that there is no need for the client to explicitly maintain an
615 expensive-to-modify list of missing objects.[a]
</p>
619 <p>Since almost all Git code currently expects any referenced object to be
620 present locally and because we do not want to force every command to do
621 a dry-run first, a fallback mechanism is added to allow Git to attempt
622 to dynamically fetch missing objects from promisor remotes.
</p>
623 <div class=
"paragraph">
624 <p>When the normal object lookup fails to find an object, Git invokes
625 promisor_remote_get_direct() to try to get the object from a promisor
626 remote and then retry the object lookup. This allows objects to be
627 "faulted in" without complicated prediction algorithms.
</p>
629 <div class=
"paragraph">
630 <p>For efficiency reasons, no check as to whether the missing object is
631 actually a promisor object is performed.
</p>
633 <div class=
"paragraph">
634 <p>Dynamic object fetching tends to be slow as objects are fetched one at
639 <p><code>checkout
</code> (and any other command using
<code>unpack-trees
</code>) has been taught
640 to bulk pre-fetch all required missing blobs in a single batch.
</p>
643 <p><code>rev-list
</code> has been taught to print missing objects.
</p>
644 <div class=
"paragraph">
645 <p>This can be used by other commands to bulk prefetch objects.
646 For example, a
"git log -p A..B" may internally want to first do
647 something like
"git rev-list --objects --quiet --missing=print A..B"
648 and prefetch those objects in bulk.
</p>
652 <p><code>fsck
</code> has been updated to be fully aware of promisor objects.
</p>
655 <p><code>repack
</code> in GC has been updated to not touch promisor packfiles at all,
656 and to only repack other objects.
</p>
659 <p>The global variable
"fetch_if_missing" is used to control whether an
660 object lookup will attempt to dynamically fetch a missing object or
662 <div class=
"paragraph">
663 <p>We are not happy with this global variable and would like to remove it,
664 but that requires significant refactoring of the object code to pass an
673 <h2 id=
"_fetching_missing_objects">Fetching Missing Objects
</h2>
674 <div class=
"sectionbody">
678 <p>Fetching of objects is done by invoking a
"git fetch" subprocess.
</p>
681 <p>The local repository sends a request with the hashes of all requested
682 objects, and does not perform any packfile negotiation.
683 It then receives a packfile.
</p>
686 <p>Because we are reusing the existing fetch mechanism, fetching
687 currently fetches all objects referred to by the requested objects, even
688 though they are not necessary.
</p>
691 <p>Fetching with
<code>--refetch
</code> will request a complete new filtered packfile from
692 the remote, which can be used to change a filter without needing to
693 dynamically fetch missing objects.
</p>
700 <h2 id=
"_using_many_promisor_remotes">Using many promisor remotes
</h2>
701 <div class=
"sectionbody">
702 <div class=
"paragraph">
703 <p>Many promisor remotes can be configured and used.
</p>
705 <div class=
"paragraph">
706 <p>This allows for example a user to have multiple geographically-close
707 cache servers for fetching missing blobs while continuing to do
708 filtered
<code>git-fetch
</code> commands from the central server.
</p>
710 <div class=
"paragraph">
711 <p>When fetching objects, promisor remotes are tried one after the other
712 until all the objects have been fetched.
</p>
714 <div class=
"paragraph">
715 <p>Remotes that are considered
"promisor" remotes are those specified by
716 the following configuration variables:
</p>
721 <p><code>extensions.partialClone
</code> <code>=
</code> <em><name
></em></p>
724 <p><code>remote.
</code><em><name
></em><code>.promisor
</code> <code>=
</code> <code>true
</code></p>
727 <p><code>remote.
</code><em><name
></em><code>.partialCloneFilter
</code> <code>=
</code> ...
</p>
731 <div class=
"paragraph">
732 <p>Only one promisor remote can be configured using the
733 <code>extensions.partialClone
</code> config variable. This promisor remote will
734 be the last one tried when fetching objects.
</p>
736 <div class=
"paragraph">
737 <p>We decided to make it the last one we try, because it is likely that
738 someone using many promisor remotes is doing so because the other
739 promisor remotes are better for some reason (maybe they are closer or
740 faster for some kind of objects) than the origin, and the origin is
741 likely to be the remote specified by extensions.partialClone.
</p>
743 <div class=
"paragraph">
744 <p>This justification is not very strong, but one choice had to be made,
745 and anyway the long term plan should be to make the order somehow
746 fully configurable.
</p>
748 <div class=
"paragraph">
749 <p>For now though the other promisor remotes will be tried in the order
750 they appear in the config file.
</p>
755 <h2 id=
"_current_limitations">Current Limitations
</h2>
756 <div class=
"sectionbody">
760 <p>It is not possible to specify the order in which the promisor
761 remotes are tried in other ways than the order in which they appear
762 in the config file.
</p>
763 <div class=
"paragraph">
764 <p>It is also not possible to specify an order to be used when fetching
765 from one remote and a different order when fetching from another
770 <p>It is not possible to push only specific objects to a promisor
772 <div class=
"paragraph">
773 <p>It is not possible to push at the same time to multiple promisor
774 remote in a specific order.
</p>
778 <p>Dynamic object fetching will only ask promisor remotes for missing
779 objects. We assume that promisor remotes have a complete view of the
780 repository and can satisfy all such requests.
</p>
783 <p>Repack essentially treats promisor and non-promisor packfiles as
2
784 distinct partitions and does not mix them.
</p>
787 <p>Dynamic object fetching invokes fetch-pack once
<strong>for each item
</strong>
788 because most algorithms stumble upon a missing object and need to have
789 it resolved before continuing their work. This may incur significant
790 overhead
 — and multiple authentication requests
 — if many objects are
794 <p>Dynamic object fetching currently uses the existing pack protocol V0
795 which means that each object is requested via fetch-pack. The server
796 will send a full set of info/refs when the connection is established.
797 If there are a large number of refs, this may incur significant overhead.
</p>
804 <h2 id=
"_future_work">Future Work
</h2>
805 <div class=
"sectionbody">
809 <p>Improve the way to specify the order in which promisor remotes are
811 <div class=
"paragraph">
812 <p>For example this could allow specifying explicitly something like:
813 "When fetching from this remote, I want to use these promisor remotes
814 in this order, though, when pushing or fetching to that remote, I want
815 to use those promisor remotes in that order."</p>
819 <p>Allow pushing to promisor remotes.
</p>
820 <div class=
"paragraph">
821 <p>The user might want to work in a triangular work flow with multiple
822 promisor remotes that each have an incomplete view of the repository.
</p>
826 <p>Allow non-pathname-based filters to make use of packfile bitmaps (when
827 present). This was just an omission during the initial implementation.
</p>
830 <p>Investigate use of a long-running process to dynamically fetch a series
831 of objects, such as proposed in [
5,
6] to reduce process startup and
833 <div class=
"paragraph">
834 <p>It would be nice if pack protocol V2 could allow that long-running
835 process to make a series of requests over a single long-running
840 <p>Investigate pack protocol V2 to avoid the info/refs broadcast on
841 each connection with the server to dynamically fetch missing objects.
</p>
844 <p>Investigate the need to handle loose promisor objects.
</p>
845 <div class=
"paragraph">
846 <p>Objects in promisor packfiles are allowed to reference missing objects
847 that can be dynamically fetched from the server. An assumption was
848 made that loose objects are only created locally and therefore should
849 not reference a missing object. We may need to revisit that assumption
850 if, for example, we dynamically fetch a missing tree and store it as a
851 loose object rather than a single object packfile.
</p>
853 <div class=
"paragraph">
854 <p>This does not necessarily mean we need to mark loose objects as promisor;
855 it may be sufficient to relax the object lookup or is-promisor functions.
</p>
863 <h2 id=
"_non_tasks">Non-Tasks
</h2>
864 <div class=
"sectionbody">
868 <p>Every time the subject of
"demand loading blobs" comes up it seems
869 that someone suggests that the server be allowed to
"guess" and send
870 additional objects that may be related to the requested objects.
</p>
871 <div class=
"paragraph">
872 <p>No work has gone into actually doing that; we
’re just documenting that
873 it is a common suggestion. We
’re not sure how it would work and have
874 no plans to work on it.
</p>
876 <div class=
"paragraph">
877 <p>It is valid for the server to send more objects than requested (even
878 for a dynamic object fetch), but we are not building on that.
</p>
886 <h2 id=
"_footnotes">Footnotes
</h2>
887 <div class=
"sectionbody">
888 <div class=
"paragraph">
889 <p>[a] expensive-to-modify list of missing objects: Earlier in the design of
890 partial clone we discussed the need for a single list of missing objects.
891 This would essentially be a sorted linear list of OIDs that were
892 omitted by the server during a clone or subsequent fetches.
</p>
894 <div class=
"paragraph">
895 <p>This file would need to be loaded into memory on every object lookup.
896 It would need to be read, updated, and re-written (like the .git/index)
897 on every explicit
"git fetch" command
<strong>and
</strong> on any dynamic object fetch.
</p>
899 <div class=
"paragraph">
900 <p>The cost to read, update, and write this file could add significant
901 overhead to every command if there are many missing objects. For example,
902 if there are
100M missing blobs, this file would be at least
2GiB on disk.
</p>
904 <div class=
"paragraph">
905 <p>With the
"promisor" concept, we
<strong>infer
</strong> a missing object based upon the
906 type of packfile that references it.
</p>
911 <h2 id=
"_related_links">Related Links
</h2>
912 <div class=
"sectionbody">
913 <div class=
"paragraph">
914 <p>[
0]
<a href=
"https://crbug.com/git/2" class=
"bare">https://crbug.com/git/
2</a>
915 Bug#
2: Partial Clone
</p>
917 <div class=
"paragraph">
918 <p>[
1]
<a href=
"https://lore.kernel.org/git/20170113155253.1644-1-benpeart@microsoft.com/" class=
"bare">https://lore.kernel.org/git/
20170113155253.1644-
1-benpeart@microsoft.com/
</a><br/>
919 Subject: [RFC] Add support for downloading blobs on demand
<br/>
920 Date: Fri,
13 Jan
2017 10:
52:
53 -
0500</p>
922 <div class=
"paragraph">
923 <p>[
2]
<a href=
"https://lore.kernel.org/git/cover.1506714999.git.jonathantanmy@google.com/" class=
"bare">https://lore.kernel.org/git/cover
.1506714999.git.jonathantanmy@google.com/
</a><br/>
924 Subject: [PATCH
00/
18] Partial clone (from clone to lazy fetch in
18 patches)
<br/>
925 Date: Fri,
29 Sep
2017 13:
11:
36 -
0700</p>
927 <div class=
"paragraph">
928 <p>[
3]
<a href=
"https://lore.kernel.org/git/20170426221346.25337-1-jonathantanmy@google.com/" class=
"bare">https://lore.kernel.org/git/
20170426221346.25337-
1-jonathantanmy@google.com/
</a><br/>
929 Subject: Proposal for missing blob support in Git repos
<br/>
930 Date: Wed,
26 Apr
2017 15:
13:
46 -
0700</p>
932 <div class=
"paragraph">
933 <p>[
4]
<a href=
"https://lore.kernel.org/git/1488999039-37631-1-git-send-email-git@jeffhostetler.com/" class=
"bare">https://lore.kernel.org/git/
1488999039-
37631-
1-git-send-email-git@jeffhostetler.com/
</a><br/>
934 Subject: [PATCH
00/
10] RFC Partial Clone and Fetch
<br/>
935 Date: Wed,
8 Mar
2017 18:
50:
29 +
0000</p>
937 <div class=
"paragraph">
938 <p>[
5]
<a href=
"https://lore.kernel.org/git/20170505152802.6724-1-benpeart@microsoft.com/" class=
"bare">https://lore.kernel.org/git/
20170505152802.6724-
1-benpeart@microsoft.com/
</a><br/>
939 Subject: [PATCH v7
00/
10] refactor the filter process code into a reusable module
<br/>
940 Date: Fri,
5 May
2017 11:
27:
52 -
0400</p>
942 <div class=
"paragraph">
943 <p>[
6]
<a href=
"https://lore.kernel.org/git/20170714132651.170708-1-benpeart@microsoft.com/" class=
"bare">https://lore.kernel.org/git/
20170714132651.170708-
1-benpeart@microsoft.com/
</a><br/>
944 Subject: [RFC/PATCH v2
0/
1] Add support for downloading blobs on demand
<br/>
945 Date: Fri,
14 Jul
2017 09:
26:
50 -
0400</p>
951 <div id=
"footer-text">
952 Last updated
2024-
11-
08 13:
36:
53 +
0900