Autogenerated HTML docs for v2.48.0
[git-htmldocs.git] / technical / bundle-uri.html
blob17d0d42c96a469951eba0a0f368e7dc677a34fe1
1 <!DOCTYPE html>
2 <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
3 <head>
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>Bundle URIs</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"/>
10 <style>
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%}
15 a{background:none}
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}
20 abbr{font-size:.9em}
21 abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
22 dfn{font-style:italic}
23 hr{height:0}
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"}
28 small{font-size:80%}
29 sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
30 sup{top:-.5em}
31 sub{bottom:-.25em}
32 img{border:0}
33 svg:not(:root){overflow:hidden}
34 figure{margin:0}
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}
61 .hide{display:none}
62 img,object,svg{display:inline-block;vertical-align:middle}
63 textarea{height:auto;min-height:50px}
64 select{width:100%}
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}
69 a img{border:0}
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}
74 h1{font-size:2.125em}
75 h2{font-size:1.6875em}
76 h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
77 h4,h5{font-size:1.125em}
78 h6{font-size:1em}
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}
97 h1{font-size:2.75em}
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}
110 .stretch{width:100%}
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}
343 .aqua{color:#00bfbf}
344 .aqua-background{background:#00fafa}
345 .black{color:#000}
346 .black-background{background:#000}
347 .blue{color:#0000bf}
348 .blue-background{background:#0000fa}
349 .fuchsia{color:#bf00bf}
350 .fuchsia-background{background:#fa00fa}
351 .gray{color:#606060}
352 .gray-background{background:#7d7d7d}
353 .green{color:#006000}
354 .green-background{background:#007d00}
355 .lime{color:#00bf00}
356 .lime-background{background:#00fa00}
357 .maroon{color:#600000}
358 .maroon-background{background:#7d0000}
359 .navy{color:#000060}
360 .navy-background{background:#00007d}
361 .olive{color:#606000}
362 .olive-background{background:#7d7d00}
363 .purple{color:#600060}
364 .purple-background{background:#7d007d}
365 .red{color:#bf0000}
366 .red-background{background:#fa0000}
367 .silver{color:#909090}
368 .silver-background{background:#bcbcbc}
369 .teal{color:#006060}
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}
400 html{font-size:80%}
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}
408 svg{max-width:100%}
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}}
434 </style>
435 </head>
436 <body class="article">
437 <div id="header">
438 <h1>Bundle URIs</h1>
439 </div>
440 <div id="content">
441 <div id="preamble">
442 <div class="sectionbody">
443 <div class="paragraph">
444 <p>Git bundles are files that store a pack-file along with some extra metadata,
445 including a set of refs and a (possibly empty) set of necessary commits. See
446 <a href="../git-bundle.html">git-bundle(1)</a> and <a href="../gitformat-bundle.html">gitformat-bundle(5)</a> for more information.</p>
447 </div>
448 <div class="paragraph">
449 <p>Bundle URIs are locations where Git can download one or more bundles in
450 order to bootstrap the object database in advance of fetching the remaining
451 objects from a remote.</p>
452 </div>
453 <div class="paragraph">
454 <p>One goal is to speed up clones and fetches for users with poor network
455 connectivity to the origin server. Another benefit is to allow heavy users,
456 such as CI build farms, to use local resources for the majority of Git data
457 and thereby reducing the load on the origin server.</p>
458 </div>
459 <div class="paragraph">
460 <p>To enable the bundle URI feature, users can specify a bundle URI using
461 command-line options or the origin server can advertise one or more URIs
462 via a protocol v2 capability.</p>
463 </div>
464 </div>
465 </div>
466 <div class="sect1">
467 <h2 id="_design_goals">Design Goals</h2>
468 <div class="sectionbody">
469 <div class="paragraph">
470 <p>The bundle URI standard aims to be flexible enough to satisfy multiple
471 workloads. The bundle provider and the Git client have several choices in
472 how they create and consume bundle URIs.</p>
473 </div>
474 <div class="ulist">
475 <ul>
476 <li>
477 <p>Bundles can have whatever name the server desires. This name could refer
478 to immutable data by using a hash of the bundle contents. However, this
479 means that a new URI will be needed after every update of the content.
480 This might be acceptable if the server is advertising the URI (and the
481 server is aware of new bundles being generated) but would not be
482 ergonomic for users using the command line option.</p>
483 </li>
484 <li>
485 <p>The bundles could be organized specifically for bootstrapping full
486 clones, but could also be organized with the intention of bootstrapping
487 incremental fetches. The bundle provider must decide on one of several
488 organization schemes to minimize client downloads during incremental
489 fetches, but the Git client can also choose whether to use bundles for
490 either of these operations.</p>
491 </li>
492 <li>
493 <p>The bundle provider can choose to support full clones, partial clones,
494 or both. The client can detect which bundles are appropriate for the
495 repository&#8217;s partial clone filter, if any.</p>
496 </li>
497 <li>
498 <p>The bundle provider can use a single bundle (for clones only), or a
499 list of bundles. When using a list of bundles, the provider can specify
500 whether or not the client needs <em>all</em> of the bundle URIs for a full
501 clone, or if <em>any</em> one of the bundle URIs is sufficient. This allows the
502 bundle provider to use different URIs for different geographies.</p>
503 </li>
504 <li>
505 <p>The bundle provider can organize the bundles using heuristics, such as
506 creation tokens, to help the client prevent downloading bundles it does
507 not need. When the bundle provider does not provide these heuristics,
508 the client can use optimizations to minimize how much of the data is
509 downloaded.</p>
510 </li>
511 <li>
512 <p>The bundle provider does not need to be associated with the Git server.
513 The client can choose to use the bundle provider without it being
514 advertised by the Git server.</p>
515 </li>
516 <li>
517 <p>The client can choose to discover bundle providers that are advertised
518 by the Git server. This could happen during <code>git</code> <code>clone</code>, during
519 <code>git</code> <code>fetch</code>, both, or neither. The user can choose which combination
520 works best for them.</p>
521 </li>
522 <li>
523 <p>The client can choose to configure a bundle provider manually at any
524 time. The client can also choose to specify a bundle provider manually
525 as a command-line option to <code>git</code> <code>clone</code>.</p>
526 </li>
527 </ul>
528 </div>
529 <div class="paragraph">
530 <p>Each repository is different and every Git server has different needs.
531 Hopefully the bundle URI feature is flexible enough to satisfy all needs.
532 If not, then the feature can be extended through its versioning mechanism.</p>
533 </div>
534 </div>
535 </div>
536 <div class="sect1">
537 <h2 id="_server_requirements">Server requirements</h2>
538 <div class="sectionbody">
539 <div class="paragraph">
540 <p>To provide a server-side implementation of bundle servers, no other parts
541 of the Git protocol are required. This allows server maintainers to use
542 static content solutions such as CDNs in order to serve the bundle files.</p>
543 </div>
544 <div class="paragraph">
545 <p>At the current scope of the bundle URI feature, all URIs are expected to
546 be HTTP(S) URLs where content is downloaded to a local file using a <code>GET</code>
547 request to that URL. The server could include authentication requirements
548 to those requests with the aim of triggering the configured credential
549 helper for secure access. (Future extensions could use "file://" URIs or
550 SSH URIs.)</p>
551 </div>
552 <div class="paragraph">
553 <p>Assuming a <code>200</code> <code>OK</code> response from the server, the content at the URL is
554 inspected. First, Git attempts to parse the file as a bundle file of
555 version 2 or higher. If the file is not a bundle, then the file is parsed
556 as a plain-text file using Git&#8217;s config parser. The key-value pairs in
557 that config file are expected to describe a list of bundle URIs. If
558 neither of these parse attempts succeed, then Git will report an error to
559 the user that the bundle URI provided erroneous data.</p>
560 </div>
561 <div class="paragraph">
562 <p>Any other data provided by the server is considered erroneous.</p>
563 </div>
564 </div>
565 </div>
566 <div class="sect1">
567 <h2 id="_bundle_lists">Bundle Lists</h2>
568 <div class="sectionbody">
569 <div class="paragraph">
570 <p>The Git server can advertise bundle URIs using a set of <code>key=value</code> pairs.
571 A bundle URI can also serve a plain-text file in the Git config format
572 containing these same <code>key=value</code> pairs. In both cases, we consider this
573 to be a <em>bundle list</em>. The pairs specify information about the bundles
574 that the client can use to make decisions for which bundles to download
575 and which to ignore.</p>
576 </div>
577 <div class="paragraph">
578 <p>A few keys focus on properties of the list itself.</p>
579 </div>
580 <div class="dlist">
581 <dl>
582 <dt class="hdlist1">bundle.version</dt>
583 <dd>
584 <p>(Required) This value provides a version number for the bundle
585 list. If a future Git change enables a feature that needs the Git
586 client to react to a new key in the bundle list file, then this version
587 will increment. The only current version number is 1, and if any other
588 value is specified then Git will fail to use this file.</p>
589 </dd>
590 <dt class="hdlist1">bundle.mode</dt>
591 <dd>
592 <p>(Required) This value has one of two values: <code>all</code> and <code>any</code>. When <code>all</code>
593 is specified, then the client should expect to need all of the listed
594 bundle URIs that match their repository&#8217;s requirements. When <code>any</code> is
595 specified, then the client should expect that any one of the bundle URIs
596 that match their repository&#8217;s requirements will suffice. Typically, the
597 <code>any</code> option is used to list a number of different bundle servers
598 located in different geographies.</p>
599 </dd>
600 <dt class="hdlist1">bundle.heuristic</dt>
601 <dd>
602 <p>If this string-valued key exists, then the bundle list is designed to
603 work well with incremental <code>git</code> <code>fetch</code> commands. The heuristic signals
604 that there are additional keys available for each bundle that help
605 determine which subset of bundles the client should download. The only
606 heuristic currently planned is <code>creationToken</code>.</p>
607 </dd>
608 </dl>
609 </div>
610 <div class="paragraph">
611 <p>The remaining keys include an <em>&lt;id&gt;</em> segment which is a server-designated
612 name for each available bundle. The <em>&lt;id&gt;</em> must contain only alphanumeric
613 and <code>-</code> characters.</p>
614 </div>
615 <div class="dlist">
616 <dl>
617 <dt class="hdlist1">bundle.&lt;id&gt;.uri</dt>
618 <dd>
619 <p>(Required) This string value is the URI for downloading bundle <em>&lt;id&gt;</em>.
620 If the URI begins with a protocol (<code>http://</code> or <code>https://</code>) then the URI
621 is absolute. Otherwise, the URI is interpreted as relative to the URI
622 used for the bundle list. If the URI begins with <code>/</code>, then that relative
623 path is relative to the domain name used for the bundle list. (This use
624 of relative paths is intended to make it easier to distribute a set of
625 bundles across a large number of servers or CDNs with different domain
626 names.)</p>
627 </dd>
628 <dt class="hdlist1">bundle.&lt;id&gt;.filter</dt>
629 <dd>
630 <p>This string value represents an object filter that should also appear in
631 the header of this bundle. The server uses this value to differentiate
632 different kinds of bundles from which the client can choose those that
633 match their object filters.</p>
634 </dd>
635 <dt class="hdlist1">bundle.&lt;id&gt;.creationToken</dt>
636 <dd>
637 <p>This value is a nonnegative 64-bit integer used for sorting the bundles
638 list. This is used to download a subset of bundles during a fetch when
639 <code>bundle.heuristic=creationToken</code>.</p>
640 </dd>
641 <dt class="hdlist1">bundle.&lt;id&gt;.location</dt>
642 <dd>
643 <p>This string value advertises a real-world location from where the bundle
644 URI is served. This can be used to present the user with an option for
645 which bundle URI to use or simply as an informative indicator of which
646 bundle URI was selected by Git. This is only valuable when
647 <code>bundle.mode</code> is <code>any</code>.</p>
648 </dd>
649 </dl>
650 </div>
651 <div class="paragraph">
652 <p>Here is an example bundle list using the Git config format:</p>
653 </div>
654 <div class="literalblock">
655 <div class="content">
656 <pre>[bundle]
657 version = 1
658 mode = all
659 heuristic = creationToken</pre>
660 </div>
661 </div>
662 <div class="literalblock">
663 <div class="content">
664 <pre>[bundle "2022-02-09-1644442601-daily"]
665 uri = https://bundles.example.com/git/git/2022-02-09-1644442601-daily.bundle
666 creationToken = 1644442601</pre>
667 </div>
668 </div>
669 <div class="literalblock">
670 <div class="content">
671 <pre>[bundle "2022-02-02-1643842562"]
672 uri = https://bundles.example.com/git/git/2022-02-02-1643842562.bundle
673 creationToken = 1643842562</pre>
674 </div>
675 </div>
676 <div class="literalblock">
677 <div class="content">
678 <pre>[bundle "2022-02-09-1644442631-daily-blobless"]
679 uri = 2022-02-09-1644442631-daily-blobless.bundle
680 creationToken = 1644442631
681 filter = blob:none</pre>
682 </div>
683 </div>
684 <div class="literalblock">
685 <div class="content">
686 <pre>[bundle "2022-02-02-1643842568-blobless"]
687 uri = /git/git/2022-02-02-1643842568-blobless.bundle
688 creationToken = 1643842568
689 filter = blob:none</pre>
690 </div>
691 </div>
692 <div class="paragraph">
693 <p>This example uses <code>bundle.mode=all</code> as well as the
694 <code>bundle.</code><em>&lt;id&gt;</em><code>.creationToken</code> heuristic. It also uses the <code>bundle.</code><em>&lt;id&gt;</em><code>.filter</code>
695 options to present two parallel sets of bundles: one for full clones and
696 another for blobless partial clones.</p>
697 </div>
698 <div class="paragraph">
699 <p>Suppose that this bundle list was found at the URI
700 <code>https://bundles.example.com/git/git/</code> and so the two blobless bundles have
701 the following fully-expanded URIs:</p>
702 </div>
703 <div class="ulist">
704 <ul>
705 <li>
706 <p><code>https://bundles.example.com/git/git/2022-02-09-1644442631-daily-blobless.bundle</code></p>
707 </li>
708 <li>
709 <p><code>https://bundles.example.com/git/git/2022-02-02-1643842568-blobless.bundle</code></p>
710 </li>
711 </ul>
712 </div>
713 </div>
714 </div>
715 <div class="sect1">
716 <h2 id="_advertising_bundle_uris">Advertising Bundle URIs</h2>
717 <div class="sectionbody">
718 <div class="paragraph">
719 <p>If a user knows a bundle URI for the repository they are cloning, then
720 they can specify that URI manually through a command-line option. However,
721 a Git host may want to advertise bundle URIs during the clone operation,
722 helping users unaware of the feature.</p>
723 </div>
724 <div class="paragraph">
725 <p>The only thing required for this feature is that the server can advertise
726 one or more bundle URIs. This advertisement takes the form of a new
727 protocol v2 capability specifically for discovering bundle URIs.</p>
728 </div>
729 <div class="paragraph">
730 <p>The client could choose an arbitrary bundle URI as an option <em>or</em> select
731 the URI with best performance by some exploratory checks. It is up to the
732 bundle provider to decide if having multiple URIs is preferable to a
733 single URI that is geodistributed through server-side infrastructure.</p>
734 </div>
735 </div>
736 </div>
737 <div class="sect1">
738 <h2 id="_cloning_with_bundle_uris">Cloning with Bundle URIs</h2>
739 <div class="sectionbody">
740 <div class="paragraph">
741 <p>The primary need for bundle URIs is to speed up clones. The Git client
742 will interact with bundle URIs according to the following flow:</p>
743 </div>
744 <div class="olist arabic">
745 <ol class="arabic">
746 <li>
747 <p>The user specifies a bundle URI with the <code>--bundle-uri</code> command-line
748 option <em>or</em> the client discovers a bundle list advertised by the
749 Git server.</p>
750 </li>
751 <li>
752 <p>If the downloaded data from a bundle URI is a bundle, then the client
753 inspects the bundle headers to check that the prerequisite commit OIDs
754 are present in the client repository. If some are missing, then the
755 client delays unbundling until other bundles have been unbundled,
756 making those OIDs present. When all required OIDs are present, the
757 client unbundles that data using a refspec. The default refspec is
758 <code>+refs/heads/</code>*:refs/bundles/*, but this can be configured. These refs
759 are stored so that later <code>git</code> <code>fetch</code> negotiations can communicate each
760 bundled ref as a <code>have</code>, reducing the size of the fetch over the Git
761 protocol. To allow pruning refs from this ref namespace, Git may
762 introduce a numbered namespace (such as <code>refs/bundles/</code><em>&lt;i&gt;</em><code>/</code>*) such that
763 stale bundle refs can be deleted.</p>
764 </li>
765 <li>
766 <p>If the file is instead a bundle list, then the client inspects the
767 <code>bundle.mode</code> to see if the list is of the <code>all</code> or <code>any</code> form.</p>
768 <div class="olist loweralpha">
769 <ol class="loweralpha">
770 <li>
771 <p>If <code>bundle.mode=all</code>, then the client considers all bundle
772 URIs. The list is reduced based on the <code>bundle.</code><em>&lt;id&gt;</em><code>.filter</code> options
773 matching the client repository&#8217;s partial clone filter. Then, all
774 bundle URIs are requested. If the <code>bundle.</code><em>&lt;id&gt;</em><code>.creationToken</code>
775 heuristic is provided, then the bundles are downloaded in decreasing
776 order by the creation token, stopping when a bundle has all required
777 OIDs. The bundles can then be unbundled in increasing creation token
778 order. The client stores the latest creation token as a heuristic
779 for avoiding future downloads if the bundle list does not advertise
780 bundles with larger creation tokens.</p>
781 </li>
782 <li>
783 <p>If <code>bundle.mode=any</code>, then the client can choose any one of the
784 bundle URIs to inspect. The client can use a variety of ways to
785 choose among these URIs. The client can also fallback to another URI
786 if the initial choice fails to return a result.</p>
787 </li>
788 </ol>
789 </div>
790 </li>
791 </ol>
792 </div>
793 <div class="paragraph">
794 <p>Note that during a clone we expect that all bundles will be required, and
795 heuristics such as <code>bundle.</code><em>&lt;uri&gt;</em><code>.creationToken</code> can be used to download
796 bundles in chronological order or in parallel.</p>
797 </div>
798 <div class="paragraph">
799 <p>If a given bundle URI is a bundle list with a <code>bundle.heuristic</code>
800 value, then the client can choose to store that URI as its chosen bundle
801 URI. The client can then navigate directly to that URI during later <code>git</code>
802 <code>fetch</code> calls.</p>
803 </div>
804 <div class="paragraph">
805 <p>When downloading bundle URIs, the client can choose to inspect the initial
806 content before committing to downloading the entire content. This may
807 provide enough information to determine if the URI is a bundle list or
808 a bundle. In the case of a bundle, the client may inspect the bundle
809 header to determine that all advertised tips are already in the client
810 repository and cancel the remaining download.</p>
811 </div>
812 </div>
813 </div>
814 <div class="sect1">
815 <h2 id="_fetching_with_bundle_uris">Fetching with Bundle URIs</h2>
816 <div class="sectionbody">
817 <div class="paragraph">
818 <p>When the client fetches new data, it can decide to fetch from bundle
819 servers before fetching from the origin remote. This could be done via a
820 command-line option, but it is more likely useful to use a config value
821 such as the one specified during the clone.</p>
822 </div>
823 <div class="paragraph">
824 <p>The fetch operation follows the same procedure to download bundles from a
825 bundle list (although we do <em>not</em> want to use parallel downloads here). We
826 expect that the process will end when all prerequisite commit OIDs in a
827 thin bundle are already in the object database.</p>
828 </div>
829 <div class="paragraph">
830 <p>When using the <code>creationToken</code> heuristic, the client can avoid downloading
831 any bundles if their creation tokens are not larger than the stored
832 creation token. After fetching new bundles, Git updates this local
833 creation token.</p>
834 </div>
835 <div class="paragraph">
836 <p>If the bundle provider does not provide a heuristic, then the client
837 should attempt to inspect the bundle headers before downloading the full
838 bundle data in case the bundle tips already exist in the client
839 repository.</p>
840 </div>
841 </div>
842 </div>
843 <div class="sect1">
844 <h2 id="_error_conditions">Error Conditions</h2>
845 <div class="sectionbody">
846 <div class="paragraph">
847 <p>If the Git client discovers something unexpected while downloading
848 information according to a bundle URI or the bundle list found at that
849 location, then Git can ignore that data and continue as if it was not
850 given a bundle URI. The remote Git server is the ultimate source of truth,
851 not the bundle URI.</p>
852 </div>
853 <div class="paragraph">
854 <p>Here are a few example error conditions:</p>
855 </div>
856 <div class="ulist">
857 <ul>
858 <li>
859 <p>The client fails to connect with a server at the given URI or a connection
860 is lost without any chance to recover.</p>
861 </li>
862 <li>
863 <p>The client receives a 400-level response (such as <code>404</code> <code>Not</code> <code>Found</code> or
864 <code>401</code> <code>Not</code> <code>Authorized</code>). The client should use the credential helper to
865 find and provide a credential for the URI, but match the semantics of
866 Git&#8217;s other HTTP protocols in terms of handling specific 400-level
867 errors.</p>
868 </li>
869 <li>
870 <p>The server reports any other failure response.</p>
871 </li>
872 <li>
873 <p>The client receives data that is not parsable as a bundle or bundle list.</p>
874 </li>
875 <li>
876 <p>A bundle includes a filter that does not match expectations.</p>
877 </li>
878 <li>
879 <p>The client cannot unbundle the bundles because the prerequisite commit OIDs
880 are not in the object database and there are no more bundles to download.</p>
881 </li>
882 </ul>
883 </div>
884 <div class="paragraph">
885 <p>There are also situations that could be seen as wasteful, but are not
886 error conditions:</p>
887 </div>
888 <div class="ulist">
889 <ul>
890 <li>
891 <p>The downloaded bundles contain more information than is requested by
892 the clone or fetch request. A primary example is if the user requests
893 a clone with <code>--single-branch</code> but downloads bundles that store every
894 reachable commit from all <code>refs/heads/</code>* references. This might be
895 initially wasteful, but perhaps these objects will become reachable by
896 a later ref update that the client cares about.</p>
897 </li>
898 <li>
899 <p>A bundle download during a <code>git</code> <code>fetch</code> contains objects already in the
900 object database. This is probably unavoidable if we are using bundles
901 for fetches, since the client will almost always be slightly ahead of
902 the bundle servers after performing its "catch-up" fetch to the remote
903 server. This extra work is most wasteful when the client is fetching
904 much more frequently than the server is computing bundles, such as if
905 the client is using hourly prefetches with background maintenance, but
906 the server is computing bundles weekly. For this reason, the client
907 should not use bundle URIs for fetch unless the server has explicitly
908 recommended it through a <code>bundle.heuristic</code> value.</p>
909 </li>
910 </ul>
911 </div>
912 </div>
913 </div>
914 <div class="sect1">
915 <h2 id="_example_bundle_provider_organization">Example Bundle Provider organization</h2>
916 <div class="sectionbody">
917 <div class="paragraph">
918 <p>The bundle URI feature is intentionally designed to be flexible to
919 different ways a bundle provider wants to organize the object data.
920 However, it can be helpful to have a complete organization model described
921 here so providers can start from that base.</p>
922 </div>
923 <div class="paragraph">
924 <p>This example organization is a simplified model of what is used by the
925 GVFS Cache Servers (see section near the end of this document) which have
926 been beneficial in speeding up clones and fetches for very large
927 repositories, although using extra software outside of Git.</p>
928 </div>
929 <div class="paragraph">
930 <p>The bundle provider deploys servers across multiple geographies. Each
931 server manages its own bundle set. The server can track a number of Git
932 repositories, but provides a bundle list for each based on a pattern. For
933 example, when mirroring a repository at <code>https://</code><em>&lt;domain&gt;</em><code>/</code><em>&lt;org&gt;</em><code>/</code><em>&lt;repo&gt;</em>
934 the bundle server could have its bundle list available at
935 <code>https://</code><em>&lt;server-url&gt;</em><code>/</code><em>&lt;domain&gt;</em><code>/</code><em>&lt;org&gt;</em><code>/</code><em>&lt;repo&gt;</em>. The origin Git server can
936 list all of these servers under the "any" mode:</p>
937 </div>
938 <div class="literalblock">
939 <div class="content">
940 <pre>[bundle]
941 version = 1
942 mode = any</pre>
943 </div>
944 </div>
945 <div class="literalblock">
946 <div class="content">
947 <pre>[bundle "eastus"]
948 uri = https://eastus.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;</pre>
949 </div>
950 </div>
951 <div class="literalblock">
952 <div class="content">
953 <pre>[bundle "europe"]
954 uri = https://europe.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;</pre>
955 </div>
956 </div>
957 <div class="literalblock">
958 <div class="content">
959 <pre>[bundle "apac"]
960 uri = https://apac.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;</pre>
961 </div>
962 </div>
963 <div class="paragraph">
964 <p>This "list of lists" is static and only changes if a bundle server is
965 added or removed.</p>
966 </div>
967 <div class="paragraph">
968 <p>Each bundle server manages its own set of bundles. The initial bundle list
969 contains only a single bundle, containing all of the objects received from
970 cloning the repository from the origin server. The list uses the
971 <code>creationToken</code> heuristic and a <code>creationToken</code> is made for the bundle
972 based on the server&#8217;s timestamp.</p>
973 </div>
974 <div class="paragraph">
975 <p>The bundle server runs regularly-scheduled updates for the bundle list,
976 such as once a day. During this task, the server fetches the latest
977 contents from the origin server and generates a bundle containing the
978 objects reachable from the latest origin refs, but not contained in a
979 previously-computed bundle. This bundle is added to the list, with care
980 that the <code>creationToken</code> is strictly greater than the previous maximum
981 <code>creationToken</code>.</p>
982 </div>
983 <div class="paragraph">
984 <p>When the bundle list grows too large, say more than 30 bundles, then the
985 oldest "<em>N</em> minus 30" bundles are combined into a single bundle. This
986 bundle&#8217;s <code>creationToken</code> is equal to the maximum <code>creationToken</code> among the
987 merged bundles.</p>
988 </div>
989 <div class="paragraph">
990 <p>An example bundle list is provided here, although it only has two daily
991 bundles and not a full list of 30:</p>
992 </div>
993 <div class="literalblock">
994 <div class="content">
995 <pre>[bundle]
996 version = 1
997 mode = all
998 heuristic = creationToken</pre>
999 </div>
1000 </div>
1001 <div class="literalblock">
1002 <div class="content">
1003 <pre>[bundle "2022-02-13-1644770820-daily"]
1004 uri = https://eastus.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;/2022-02-09-1644770820-daily.bundle
1005 creationToken = 1644770820</pre>
1006 </div>
1007 </div>
1008 <div class="literalblock">
1009 <div class="content">
1010 <pre>[bundle "2022-02-09-1644442601-daily"]
1011 uri = https://eastus.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;/2022-02-09-1644442601-daily.bundle
1012 creationToken = 1644442601</pre>
1013 </div>
1014 </div>
1015 <div class="literalblock">
1016 <div class="content">
1017 <pre>[bundle "2022-02-02-1643842562"]
1018 uri = https://eastus.example.com/&lt;domain&gt;/&lt;org&gt;/&lt;repo&gt;/2022-02-02-1643842562.bundle
1019 creationToken = 1643842562</pre>
1020 </div>
1021 </div>
1022 <div class="paragraph">
1023 <p>To avoid storing and serving object data in perpetuity despite becoming
1024 unreachable in the origin server, this bundle merge can be more careful.
1025 Instead of taking an absolute union of the old bundles, instead the bundle
1026 can be created by looking at the newer bundles and ensuring that their
1027 necessary commits are all available in this merged bundle (or in another
1028 one of the newer bundles). This allows "expiring" object data that is not
1029 being used by new commits in this window of time. That data could be
1030 reintroduced by a later push.</p>
1031 </div>
1032 <div class="paragraph">
1033 <p>The intention of this data organization has two main goals. First, initial
1034 clones of the repository become faster by downloading precomputed object
1035 data from a closer source. Second, <code>git</code> <code>fetch</code> commands can be faster,
1036 especially if the client has not fetched for a few days. However, if a
1037 client does not fetch for 30 days, then the bundle list organization would
1038 cause redownloading a large amount of object data.</p>
1039 </div>
1040 <div class="paragraph">
1041 <p>One way to make this organization more useful to users who fetch frequently
1042 is to have more frequent bundle creation. For example, bundles could be
1043 created every hour, and then once a day those "hourly" bundles could be
1044 merged into a "daily" bundle. The daily bundles are merged into the
1045 oldest bundle after 30 days.</p>
1046 </div>
1047 <div class="paragraph">
1048 <p>It is recommended that this bundle strategy is repeated with the <code>blob:none</code>
1049 filter if clients of this repository are expecting to use blobless partial
1050 clones. This list of blobless bundles stays in the same list as the full
1051 bundles, but uses the <code>bundle.</code><em>&lt;id&gt;</em><code>.filter</code> key to separate the two groups.
1052 For very large repositories, the bundle provider may want to <em>only</em> provide
1053 blobless bundles.</p>
1054 </div>
1055 </div>
1056 </div>
1057 <div class="sect1">
1058 <h2 id="_implementation_plan">Implementation Plan</h2>
1059 <div class="sectionbody">
1060 <div class="paragraph">
1061 <p>This design document is being submitted on its own as an aspirational
1062 document, with the goal of implementing all of the mentioned client
1063 features over the course of several patch series. Here is a potential
1064 outline for submitting these features:</p>
1065 </div>
1066 <div class="olist arabic">
1067 <ol class="arabic">
1068 <li>
1069 <p>Integrate bundle URIs into <code>git</code> <code>clone</code> with a <code>--bundle-uri</code> option.
1070 This will include a new <code>git</code> <code>fetch</code> <code>--bundle-uri</code> mode for use as the
1071 implementation underneath <code>git</code> <code>clone</code>. The initial version here will
1072 expect a single bundle at the given URI.</p>
1073 </li>
1074 <li>
1075 <p>Implement the ability to parse a bundle list from a bundle URI and
1076 update the <code>git</code> <code>fetch</code> <code>--bundle-uri</code> logic to properly distinguish
1077 between <code>bundle.mode</code> options. Specifically design the feature so
1078 that the config format parsing feeds a list of key-value pairs into the
1079 bundle list logic.</p>
1080 </li>
1081 <li>
1082 <p>Create the <code>bundle-uri</code> protocol v2 command so Git servers can advertise
1083 bundle URIs using the key-value pairs. Plug into the existing key-value
1084 input to the bundle list logic. Allow <code>git</code> <code>clone</code> to discover these
1085 bundle URIs and bootstrap the client repository from the bundle data.
1086 (This choice is an opt-in via a config option and a command-line
1087 option.)</p>
1088 </li>
1089 <li>
1090 <p>Allow the client to understand the <code>bundle.heuristic</code> configuration key
1091 and the <code>bundle.</code><em>&lt;id&gt;</em><code>.creationToken</code> heuristic. When <code>git</code> <code>clone</code>
1092 discovers a bundle URI with <code>bundle.heuristic</code>, it configures the client
1093 repository to check that bundle URI during later <code>git</code> <code>fetch</code> <em>&lt;remote&gt;</em>
1094 commands.</p>
1095 </li>
1096 <li>
1097 <p>Allow clients to discover bundle URIs during <code>git</code> <code>fetch</code> and configure
1098 a bundle URI for later fetches if <code>bundle.heuristic</code> is set.</p>
1099 </li>
1100 <li>
1101 <p>Implement the "inspect headers" heuristic to reduce data downloads when
1102 the <code>bundle.</code><em>&lt;id&gt;</em><code>.creationToken</code> heuristic is not available.</p>
1103 </li>
1104 </ol>
1105 </div>
1106 <div class="paragraph">
1107 <p>As these features are reviewed, this plan might be updated. We also expect
1108 that new designs will be discovered and implemented as this feature
1109 matures and becomes used in real-world scenarios.</p>
1110 </div>
1111 </div>
1112 </div>
1113 <div class="sect1">
1114 <h2 id="_related_work_packfile_uris">Related Work: Packfile URIs</h2>
1115 <div class="sectionbody">
1116 <div class="paragraph">
1117 <p>The Git protocol already has a capability where the Git server can list
1118 a set of URLs along with the packfile response when serving a client
1119 request. The client is then expected to download the packfiles at those
1120 locations in order to have a complete understanding of the response.</p>
1121 </div>
1122 <div class="paragraph">
1123 <p>This mechanism is used by the Gerrit server (implemented with JGit) and
1124 has been effective at reducing CPU load and improving user performance for
1125 clones.</p>
1126 </div>
1127 <div class="paragraph">
1128 <p>A major downside to this mechanism is that the origin server needs to know
1129 <em>exactly</em> what is in those packfiles, and the packfiles need to be available
1130 to the user for some time after the server has responded. This coupling
1131 between the origin and the packfile data is difficult to manage.</p>
1132 </div>
1133 <div class="paragraph">
1134 <p>Further, this implementation is extremely hard to make work with fetches.</p>
1135 </div>
1136 </div>
1137 </div>
1138 <div class="sect1">
1139 <h2 id="_related_work_gvfs_cache_servers">Related Work: GVFS Cache Servers</h2>
1140 <div class="sectionbody">
1141 <div class="paragraph">
1142 <p>The GVFS Protocol [2] is a set of HTTP endpoints designed independently of
1143 the Git project before Git&#8217;s partial clone was created. One feature of this
1144 protocol is the idea of a "cache server" which can be colocated with build
1145 machines or developer offices to transfer Git data without overloading the
1146 central server.</p>
1147 </div>
1148 <div class="paragraph">
1149 <p>The endpoint that VFS for Git is famous for is the <code>GET</code> <code>/gvfs/objects/</code>{oid}
1150 endpoint, which allows downloading an object on-demand. This is a critical
1151 piece of the filesystem virtualization of that product.</p>
1152 </div>
1153 <div class="paragraph">
1154 <p>However, a more subtle need is the <code>GET</code> <code>/gvfs/prefetch</code>?lastPackTimestamp=<em>&lt;t&gt;</em>
1155 endpoint. Given an optional timestamp, the cache server responds with a list
1156 of precomputed packfiles containing the commits and trees that were introduced
1157 in those time intervals.</p>
1158 </div>
1159 <div class="paragraph">
1160 <p>The cache server computes these "prefetch" packfiles using the following
1161 strategy:</p>
1162 </div>
1163 <div class="olist arabic">
1164 <ol class="arabic">
1165 <li>
1166 <p>Every hour, an "hourly" pack is generated with a given timestamp.</p>
1167 </li>
1168 <li>
1169 <p>Nightly, the previous 24 hourly packs are rolled up into a "daily" pack.</p>
1170 </li>
1171 <li>
1172 <p>Nightly, all prefetch packs more than 30 days old are rolled up into
1173 one pack.</p>
1174 </li>
1175 </ol>
1176 </div>
1177 <div class="paragraph">
1178 <p>When a user runs <code>gvfs</code> <code>clone</code> or <code>scalar</code> <code>clone</code> against a repo with cache
1179 servers, the client requests all prefetch packfiles, which is at most
1180 <code>24</code> <code>+</code> <code>30</code> <code>+</code> <code>1</code> packfiles downloading only commits and trees. The client
1181 then follows with a request to the origin server for the references, and
1182 attempts to checkout that tip reference. (There is an extra endpoint that
1183 helps get all reachable trees from a given commit, in case that commit
1184 was not already in a prefetch packfile.)</p>
1185 </div>
1186 <div class="paragraph">
1187 <p>During a <code>git</code> <code>fetch</code>, a hook requests the prefetch endpoint using the
1188 most-recent timestamp from a previously-downloaded prefetch packfile.
1189 Only the list of packfiles with later timestamps are downloaded. Most
1190 users fetch hourly, so they get at most one hourly prefetch pack. Users
1191 whose machines have been off or otherwise have not fetched in over 30 days
1192 might redownload all prefetch packfiles. This is rare.</p>
1193 </div>
1194 <div class="paragraph">
1195 <p>It is important to note that the clients always contact the origin server
1196 for the refs advertisement, so the refs are frequently "ahead" of the
1197 prefetched pack data. The missing objects are downloaded on-demand using
1198 the <code>GET</code> <code>gvfs/objects/</code>{oid} requests, when needed by a command such as
1199 <code>git</code> <code>checkout</code> or <code>git</code> <code>log</code>. Some Git optimizations disable checks that
1200 would cause these on-demand downloads to be too aggressive.</p>
1201 </div>
1202 </div>
1203 </div>
1204 <div class="sect1">
1205 <h2 id="_see_also">See Also</h2>
1206 <div class="sectionbody">
1207 <div class="paragraph">
1208 <p>[1] <a href="https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/" class="bare">https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/</a>
1209 An earlier RFC for a bundle URI feature.</p>
1210 </div>
1211 <div class="paragraph">
1212 <p>[2] <a href="https://github.com/microsoft/VFSForGit/blob/master/Protocol.md" class="bare">https://github.com/microsoft/VFSForGit/blob/master/Protocol.md</a>
1213 The GVFS Protocol</p>
1214 </div>
1215 </div>
1216 </div>
1217 </div>
1218 <div id="footer">
1219 <div id="footer-text">
1220 Last updated 2023-02-22 15:29:29 -0800
1221 </div>
1222 </div>
1223 </body>
1224 </html>