1Partial Clone Design Notes 2========================== 3 4The "Partial Clone" feature is a performance optimization for Git that 5allows Git to function without having a complete copy of the repository. 6The goal of this work is to allow Git better handle extremely large 7repositories. 8 9During clone and fetch operations, Git downloads the complete contents 10and history of the repository. This includes all commits, trees, and 11blobs for the complete life of the repository. For extremely large 12repositories, clones can take hours (or days) and consume 100+GiB of disk 13space. 14 15Often in these repositories there are many blobs and trees that the user 16does not need such as: 17 18 1. files outside of the user's work area in the tree. For example, in 19 a repository with 500K directories and 3.5M files in every commit, 20 we can avoid downloading many objects if the user only needs a 21 narrow "cone" of the source tree. 22 23 2. large binary assets. For example, in a repository where large build 24 artifacts are checked into the tree, we can avoid downloading all 25 previous versions of these non-mergeable binary assets and only 26 download versions that are actually referenced. 27 28Partial clone allows us to avoid downloading such unneeded objects *in 29advance* during clone and fetch operations and thereby reduce download 30times and disk usage. Missing objects can later be "demand fetched" 31if/when needed. 32 33Use of partial clone requires that the user be online and the origin 34remote be available for on-demand fetching of missing objects. This may 35or may not be problematic for the user. For example, if the user can 36stay within the pre-selected subset of the source tree, they may not 37encounter any missing objects. Alternatively, the user could try to 38pre-fetch various objects if they know that they are going offline. 39 40 41Non-Goals 42--------- 43 44Partial clone is a mechanism to limit the number of blobs and trees downloaded 45*within* a given range of commits -- and is therefore independent of and not 46intended to conflict with existing DAG-level mechanisms to limit the set of 47requested commits (i.e. shallow clone, single branch, or fetch '<refspec>'). 48 49 50Design Overview 51--------------- 52 53Partial clone logically consists of the following parts: 54 55- A mechanism for the client to describe unneeded or unwanted objects to 56 the server. 57 58- A mechanism for the server to omit such unwanted objects from packfiles 59 sent to the client. 60 61- A mechanism for the client to gracefully handle missing objects (that 62 were previously omitted by the server). 63 64- A mechanism for the client to backfill missing objects as needed. 65 66 67Design Details 68-------------- 69 70- A new pack-protocol capability "filter" is added to the fetch-pack and 71 upload-pack negotiation. 72 73 This uses the existing capability discovery mechanism. 74 See "filter" in Documentation/technical/pack-protocol.txt. 75 76- Clients pass a "filter-spec" to clone and fetch which is passed to the 77 server to request filtering during packfile construction. 78 79 There are various filters available to accommodate different situations. 80 See "--filter=<filter-spec>" in Documentation/rev-list-options.txt. 81 82- On the server pack-objects applies the requested filter-spec as it 83 creates "filtered" packfiles for the client. 84 85 These filtered packfiles are *incomplete* in the traditional sense because 86 they may contain objects that reference objects not contained in the 87 packfile and that the client doesn't already have. For example, the 88 filtered packfile may contain trees or tags that reference missing blobs 89 or commits that reference missing trees. 90 91- On the client these incomplete packfiles are marked as "promisor packfiles" 92 and treated differently by various commands. 93 94- On the client a repository extension is added to the local config to 95 prevent older versions of git from failing mid-operation because of 96 missing objects that they cannot handle. 97 See "extensions.partialClone" in Documentation/technical/repository-version.txt" 98 99 100Handling Missing Objects 101------------------------ 102 103- An object may be missing due to a partial clone or fetch, or missing due 104 to repository corruption. To differentiate these cases, the local 105 repository specially indicates such filtered packfiles obtained from the 106 promisor remote as "promisor packfiles". 107 108 These promisor packfiles consist of a "<name>.promisor" file with 109 arbitrary contents (like the "<name>.keep" files), in addition to 110 their "<name>.pack" and "<name>.idx" files. 111 112- The local repository considers a "promisor object" to be an object that 113 it knows (to the best of its ability) that the promisor remote has promised 114 that it has, either because the local repository has that object in one of 115 its promisor packfiles, or because another promisor object refers to it. 116 117 When Git encounters a missing object, Git can see if it a promisor object 118 and handle it appropriately. If not, Git can report a corruption. 119 120 This means that there is no need for the client to explicitly maintain an 121 expensive-to-modify list of missing objects.[a] 122 123- Since almost all Git code currently expects any referenced object to be 124 present locally and because we do not want to force every command to do 125 a dry-run first, a fallback mechanism is added to allow Git to attempt 126 to dynamically fetch missing objects from the promisor remote. 127 128 When the normal object lookup fails to find an object, Git invokes 129 fetch-object to try to get the object from the server and then retry 130 the object lookup. This allows objects to be "faulted in" without 131 complicated prediction algorithms. 132 133 For efficiency reasons, no check as to whether the missing object is 134 actually a promisor object is performed. 135 136 Dynamic object fetching tends to be slow as objects are fetched one at 137 a time. 138 139- `checkout` (and any other command using `unpack-trees`) has been taught 140 to bulk pre-fetch all required missing blobs in a single batch. 141 142- `rev-list` has been taught to print missing objects. 143 144 This can be used by other commands to bulk prefetch objects. 145 For example, a "git log -p A..B" may internally want to first do 146 something like "git rev-list --objects --quiet --missing=print A..B" 147 and prefetch those objects in bulk. 148 149- `fsck` has been updated to be fully aware of promisor objects. 150 151- `repack` in GC has been updated to not touch promisor packfiles at all, 152 and to only repack other objects. 153 154- The global variable "fetch_if_missing" is used to control whether an 155 object lookup will attempt to dynamically fetch a missing object or 156 report an error. 157 158 We are not happy with this global variable and would like to remove it, 159 but that requires significant refactoring of the object code to pass an 160 additional flag. We hope that concurrent efforts to add an ODB API can 161 encompass this. 162 163 164Fetching Missing Objects 165------------------------ 166 167- Fetching of objects is done using the existing transport mechanism using 168 transport_fetch_refs(), setting a new transport option 169 TRANS_OPT_NO_DEPENDENTS to indicate that only the objects themselves are 170 desired, not any object that they refer to. 171 172 Because some transports invoke fetch_pack() in the same process, fetch_pack() 173 has been updated to not use any object flags when the corresponding argument 174 (no_dependents) is set. 175 176- The local repository sends a request with the hashes of all requested 177 objects as "want" lines, and does not perform any packfile negotiation. 178 It then receives a packfile. 179 180- Because we are reusing the existing fetch-pack mechanism, fetching 181 currently fetches all objects referred to by the requested objects, even 182 though they are not necessary. 183 184 185Current Limitations 186------------------- 187 188- The remote used for a partial clone (or the first partial fetch 189 following a regular clone) is marked as the "promisor remote". 190 191 We are currently limited to a single promisor remote and only that 192 remote may be used for subsequent partial fetches. 193 194 We accept this limitation because we believe initial users of this 195 feature will be using it on repositories with a strong single central 196 server. 197 198- Dynamic object fetching will only ask the promisor remote for missing 199 objects. We assume that the promisor remote has a complete view of the 200 repository and can satisfy all such requests. 201 202- Repack essentially treats promisor and non-promisor packfiles as 2 203 distinct partitions and does not mix them. Repack currently only works 204 on non-promisor packfiles and loose objects. 205 206- Dynamic object fetching invokes fetch-pack once *for each item* 207 because most algorithms stumble upon a missing object and need to have 208 it resolved before continuing their work. This may incur significant 209 overhead -- and multiple authentication requests -- if many objects are 210 needed. 211 212- Dynamic object fetching currently uses the existing pack protocol V0 213 which means that each object is requested via fetch-pack. The server 214 will send a full set of info/refs when the connection is established. 215 If there are large number of refs, this may incur significant overhead. 216 217 218Future Work 219----------- 220 221- Allow more than one promisor remote and define a strategy for fetching 222 missing objects from specific promisor remotes or of iterating over the 223 set of promisor remotes until a missing object is found. 224 225 A user might want to have multiple geographically-close cache servers 226 for fetching missing blobs while continuing to do filtered `git-fetch` 227 commands from the central server, for example. 228 229 Or the user might want to work in a triangular work flow with multiple 230 promisor remotes that each have an incomplete view of the repository. 231 232- Allow repack to work on promisor packfiles (while keeping them distinct 233 from non-promisor packfiles). 234 235- Allow non-pathname-based filters to make use of packfile bitmaps (when 236 present). This was just an omission during the initial implementation. 237 238- Investigate use of a long-running process to dynamically fetch a series 239 of objects, such as proposed in [5,6] to reduce process startup and 240 overhead costs. 241 242 It would be nice if pack protocol V2 could allow that long-running 243 process to make a series of requests over a single long-running 244 connection. 245 246- Investigate pack protocol V2 to avoid the info/refs broadcast on 247 each connection with the server to dynamically fetch missing objects. 248 249- Investigate the need to handle loose promisor objects. 250 251 Objects in promisor packfiles are allowed to reference missing objects 252 that can be dynamically fetched from the server. An assumption was 253 made that loose objects are only created locally and therefore should 254 not reference a missing object. We may need to revisit that assumption 255 if, for example, we dynamically fetch a missing tree and store it as a 256 loose object rather than a single object packfile. 257 258 This does not necessarily mean we need to mark loose objects as promisor; 259 it may be sufficient to relax the object lookup or is-promisor functions. 260 261 262Non-Tasks 263--------- 264 265- Every time the subject of "demand loading blobs" comes up it seems 266 that someone suggests that the server be allowed to "guess" and send 267 additional objects that may be related to the requested objects. 268 269 No work has gone into actually doing that; we're just documenting that 270 it is a common suggestion. We're not sure how it would work and have 271 no plans to work on it. 272 273 It is valid for the server to send more objects than requested (even 274 for a dynamic object fetch), but we are not building on that. 275 276 277Footnotes 278--------- 279 280[a] expensive-to-modify list of missing objects: Earlier in the design of 281 partial clone we discussed the need for a single list of missing objects. 282 This would essentially be a sorted linear list of OIDs that the were 283 omitted by the server during a clone or subsequent fetches. 284 285 This file would need to be loaded into memory on every object lookup. 286 It would need to be read, updated, and re-written (like the .git/index) 287 on every explicit "git fetch" command *and* on any dynamic object fetch. 288 289 The cost to read, update, and write this file could add significant 290 overhead to every command if there are many missing objects. For example, 291 if there are 100M missing blobs, this file would be at least 2GiB on disk. 292 293 With the "promisor" concept, we *infer* a missing object based upon the 294 type of packfile that references it. 295 296 297Related Links 298------------- 299[0] https://bugs.chromium.org/p/git/issues/detail?id=2 300 Chromium work item for: Partial Clone 301 302[1] https://public-inbox.org/git/20170113155253.1644-1-benpeart@microsoft.com/ 303 Subject: [RFC] Add support for downloading blobs on demand 304 Date: Fri, 13 Jan 2017 10:52:53 -0500 305 306[2] https://public-inbox.org/git/cover.1506714999.git.jonathantanmy@google.com/ 307 Subject: [PATCH 00/18] Partial clone (from clone to lazy fetch in 18 patches) 308 Date: Fri, 29 Sep 2017 13:11:36 -0700 309 310[3] https://public-inbox.org/git/20170426221346.25337-1-jonathantanmy@google.com/ 311 Subject: Proposal for missing blob support in Git repos 312 Date: Wed, 26 Apr 2017 15:13:46 -0700 313 314[4] https://public-inbox.org/git/1488999039-37631-1-git-send-email-git@jeffhostetler.com/ 315 Subject: [PATCH 00/10] RFC Partial Clone and Fetch 316 Date: Wed, 8 Mar 2017 18:50:29 +0000 317 318[5] https://public-inbox.org/git/20170505152802.6724-1-benpeart@microsoft.com/ 319 Subject: [PATCH v7 00/10] refactor the filter process code into a reusable module 320 Date: Fri, 5 May 2017 11:27:52 -0400 321 322[6] https://public-inbox.org/git/20170714132651.170708-1-benpeart@microsoft.com/ 323 Subject: [RFC/PATCH v2 0/1] Add support for downloading blobs on demand 324 Date: Fri, 14 Jul 2017 09:26:50 -0400