diff options
author | Christopher Larson <kergoth@gmail.com> | 2017-05-13 02:46:28 +0500 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-06-02 13:36:57 +0100 |
commit | 27d56982c7ba05e86a100b0cca2411ee5ac7a85e (patch) | |
tree | 61d9ca5571770cfd093b327a7f9b7ca7ad3b56fe /bitbake/lib/bb/fetch2 | |
parent | 2a60c406372d400437ecaa8712e6dc80b3d9fcec (diff) | |
download | poky-27d56982c7ba05e86a100b0cca2411ee5ac7a85e.tar.gz |
bitbake: fetch/git: add support for shallow mirror tarballs
This adds support to the git fetcher for fetching, using, and generating
mirror tarballs of shallow git repositories. The external git-make-shallow
script is used for shallow mirror tarball creation.
This implements support for shallow mirror tarballs, not shallow clones.
Supporting shallow clones directly is not really doable for us, as we'd need
to hardcode the depth between branch HEAD and the SRCREV, and that depth would
change as the branch is updated.
When BB_GIT_SHALLOW is enabled, we will always attempt to fetch a shallow
mirror tarball. If the shallow mirror tarball cannot be fetched, it will try
to fetch the full mirror tarball and use that. If a shallow tarball is to be
used, it will be unpacked directly at `do_unpack` time, rather than extracting
it to DL_DIR at `do_fetch` time and cloning from there, to keep things simple.
There's no value in keeping a shallow repository in DL_DIR, and dealing with
the state for when to convert the clonedir to/from shallow is not worthwhile.
To clarify when shallow is used vs a real repository, a current clone is
preferred to either tarball, a shallow tarball is preferred to an out of date
clone, and a missing clone will use either tarball (attempting the shallow one
first).
All referenced branches are truncated to SRCREV (that is, commits *after*
SRCREV but before HEAD are removed) to further shrink the repository. By
default, the shallow construction process removes all unused refs
(branches/tags) from the repository, other than those referenced by the URL.
Example usage:
BB_GIT_SHALLOW ?= "1"
# Keep only the top commit
BB_GIT_SHALLOW_DEPTH ?= "1"
# This defaults to enabled if both BB_GIT_SHALLOW and
# BB_GENERATE_MIRROR_TARBALLS are enabled
BB_GENERATE_SHALLOW_TARBALLS ?= "1"
(Bitbake rev: 5ed7d85fda7c671be10ec24d7981b87a7d0d3366)
Signed-off-by: Christopher Larson <chris_larson@mentor.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/fetch2')
-rw-r--r-- | bitbake/lib/bb/fetch2/git.py | 133 |
1 files changed, 118 insertions, 15 deletions
diff --git a/bitbake/lib/bb/fetch2/git.py b/bitbake/lib/bb/fetch2/git.py index 01d4bbdc2e..0412f9ff51 100644 --- a/bitbake/lib/bb/fetch2/git.py +++ b/bitbake/lib/bb/fetch2/git.py | |||
@@ -73,8 +73,9 @@ Supported SRC_URI options are: | |||
73 | import errno | 73 | import errno |
74 | import os | 74 | import os |
75 | import re | 75 | import re |
76 | import subprocess | ||
77 | import tempfile | ||
76 | import bb | 78 | import bb |
77 | import errno | ||
78 | import bb.progress | 79 | import bb.progress |
79 | from bb.fetch2 import FetchMethod | 80 | from bb.fetch2 import FetchMethod |
80 | from bb.fetch2 import runfetchcmd | 81 | from bb.fetch2 import runfetchcmd |
@@ -172,6 +173,11 @@ class Git(FetchMethod): | |||
172 | branches = ud.parm.get("branch", "master").split(',') | 173 | branches = ud.parm.get("branch", "master").split(',') |
173 | if len(branches) != len(ud.names): | 174 | if len(branches) != len(ud.names): |
174 | raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url) | 175 | raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url) |
176 | |||
177 | ud.cloneflags = "-s -n" | ||
178 | if ud.bareclone: | ||
179 | ud.cloneflags += " --mirror" | ||
180 | |||
175 | ud.branches = {} | 181 | ud.branches = {} |
176 | for pos, name in enumerate(ud.names): | 182 | for pos, name in enumerate(ud.names): |
177 | branch = branches[pos] | 183 | branch = branches[pos] |
@@ -183,7 +189,9 @@ class Git(FetchMethod): | |||
183 | 189 | ||
184 | ud.basecmd = d.getVar("FETCHCMD_git") or "git -c core.fsyncobjectfiles=0" | 190 | ud.basecmd = d.getVar("FETCHCMD_git") or "git -c core.fsyncobjectfiles=0" |
185 | 191 | ||
186 | ud.write_tarballs = ((d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0") != "0") or ud.rebaseable | 192 | write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0" |
193 | ud.write_tarballs = write_tarballs != "0" or ud.rebaseable | ||
194 | ud.write_shallow_tarballs = (d.getVar("BB_GENERATE_SHALLOW_TARBALLS") or write_tarballs) != "0" | ||
187 | 195 | ||
188 | ud.setup_revisions(d) | 196 | ud.setup_revisions(d) |
189 | 197 | ||
@@ -205,13 +213,48 @@ class Git(FetchMethod): | |||
205 | if ud.rebaseable: | 213 | if ud.rebaseable: |
206 | for name in ud.names: | 214 | for name in ud.names: |
207 | gitsrcname = gitsrcname + '_' + ud.revisions[name] | 215 | gitsrcname = gitsrcname + '_' + ud.revisions[name] |
216 | |||
217 | dl_dir = d.getVar("DL_DIR") | ||
218 | gitdir = d.getVar("GITDIR") or (dl_dir + "/git2/") | ||
219 | ud.clonedir = os.path.join(gitdir, gitsrcname) | ||
220 | ud.localfile = ud.clonedir | ||
221 | |||
208 | mirrortarball = 'git2_%s.tar.gz' % gitsrcname | 222 | mirrortarball = 'git2_%s.tar.gz' % gitsrcname |
209 | ud.fullmirror = os.path.join(d.getVar("DL_DIR"), mirrortarball) | 223 | ud.fullmirror = os.path.join(dl_dir, mirrortarball) |
210 | ud.mirrortarballs = [mirrortarball] | 224 | ud.mirrortarballs = [mirrortarball] |
211 | gitdir = d.getVar("GITDIR") or (d.getVar("DL_DIR") + "/git2/") | ||
212 | ud.clonedir = os.path.join(gitdir, gitsrcname) | ||
213 | 225 | ||
214 | ud.localfile = ud.clonedir | 226 | ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1" |
227 | if ud.shallow: | ||
228 | ud.shallow_depth = d.getVar("BB_GIT_SHALLOW_DEPTH") | ||
229 | if ud.shallow_depth is not None: | ||
230 | try: | ||
231 | ud.shallow_depth = int(ud.shallow_depth or 0) | ||
232 | except ValueError: | ||
233 | raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % ud.shallow_depth) | ||
234 | else: | ||
235 | if not ud.shallow_depth: | ||
236 | ud.shallow = False | ||
237 | elif ud.shallow_depth < 0: | ||
238 | raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % ud.shallow_depth) | ||
239 | else: | ||
240 | ud.shallow_depth = 1 | ||
241 | |||
242 | if ud.shallow: | ||
243 | tarballname = gitsrcname | ||
244 | if ud.bareclone: | ||
245 | tarballname = "%s_bare" % tarballname | ||
246 | |||
247 | for name, revision in sorted(ud.revisions.items()): | ||
248 | tarballname = "%s_%s" % (tarballname, ud.revisions[name][:7]) | ||
249 | if not ud.nobranch: | ||
250 | tarballname = "%s-%s" % (tarballname, ud.branches[name]) | ||
251 | |||
252 | tarballname = "%s-%s" % (tarballname, ud.shallow_depth) | ||
253 | |||
254 | fetcher = self.__class__.__name__.lower() | ||
255 | ud.shallowtarball = '%sshallow_%s.tar.gz' % (fetcher, tarballname) | ||
256 | ud.fullshallow = os.path.join(dl_dir, ud.shallowtarball) | ||
257 | ud.mirrortarballs.insert(0, ud.shallowtarball) | ||
215 | 258 | ||
216 | def localpath(self, ud, d): | 259 | def localpath(self, ud, d): |
217 | return ud.clonedir | 260 | return ud.clonedir |
@@ -222,6 +265,8 @@ class Git(FetchMethod): | |||
222 | for name in ud.names: | 265 | for name in ud.names: |
223 | if not self._contains_ref(ud, d, name, ud.clonedir): | 266 | if not self._contains_ref(ud, d, name, ud.clonedir): |
224 | return True | 267 | return True |
268 | if ud.shallow and ud.write_shallow_tarballs and not os.path.exists(ud.fullshallow): | ||
269 | return True | ||
225 | if ud.write_tarballs and not os.path.exists(ud.fullmirror): | 270 | if ud.write_tarballs and not os.path.exists(ud.fullmirror): |
226 | return True | 271 | return True |
227 | return False | 272 | return False |
@@ -238,8 +283,16 @@ class Git(FetchMethod): | |||
238 | def download(self, ud, d): | 283 | def download(self, ud, d): |
239 | """Fetch url""" | 284 | """Fetch url""" |
240 | 285 | ||
241 | # If the checkout doesn't exist and the mirror tarball does, extract it | 286 | no_clone = not os.path.exists(ud.clonedir) |
242 | if not os.path.exists(ud.clonedir) and os.path.exists(ud.fullmirror): | 287 | need_update = no_clone or self.need_update(ud, d) |
288 | |||
289 | # A current clone is preferred to either tarball, a shallow tarball is | ||
290 | # preferred to an out of date clone, and a missing clone will use | ||
291 | # either tarball. | ||
292 | if ud.shallow and os.path.exists(ud.fullshallow) and need_update: | ||
293 | ud.localpath = ud.fullshallow | ||
294 | return | ||
295 | elif os.path.exists(ud.fullmirror) and no_clone: | ||
243 | bb.utils.mkdirhier(ud.clonedir) | 296 | bb.utils.mkdirhier(ud.clonedir) |
244 | runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir) | 297 | runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir) |
245 | 298 | ||
@@ -285,9 +338,21 @@ class Git(FetchMethod): | |||
285 | raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name])) | 338 | raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name])) |
286 | 339 | ||
287 | def build_mirror_data(self, ud, d): | 340 | def build_mirror_data(self, ud, d): |
288 | # Generate a mirror tarball if needed | 341 | if ud.shallow and ud.write_shallow_tarballs: |
289 | if ud.write_tarballs and not os.path.exists(ud.fullmirror): | 342 | if not os.path.exists(ud.fullshallow): |
290 | # it's possible that this symlink points to read-only filesystem with PREMIRROR | 343 | if os.path.islink(ud.fullshallow): |
344 | os.unlink(ud.fullshallow) | ||
345 | tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR')) | ||
346 | shallowclone = os.path.join(tempdir, 'git') | ||
347 | try: | ||
348 | self.clone_shallow_local(ud, shallowclone, d) | ||
349 | |||
350 | logger.info("Creating tarball of git repository") | ||
351 | runfetchcmd("tar -czf %s ." % ud.fullshallow, d, workdir=shallowclone) | ||
352 | runfetchcmd("touch %s.done" % ud.fullshallow, d) | ||
353 | finally: | ||
354 | bb.utils.remove(tempdir, recurse=True) | ||
355 | elif ud.write_tarballs and not os.path.exists(ud.fullmirror): | ||
291 | if os.path.islink(ud.fullmirror): | 356 | if os.path.islink(ud.fullmirror): |
292 | os.unlink(ud.fullmirror) | 357 | os.unlink(ud.fullmirror) |
293 | 358 | ||
@@ -295,6 +360,43 @@ class Git(FetchMethod): | |||
295 | runfetchcmd("tar -czf %s ." % ud.fullmirror, d, workdir=ud.clonedir) | 360 | runfetchcmd("tar -czf %s ." % ud.fullmirror, d, workdir=ud.clonedir) |
296 | runfetchcmd("touch %s.done" % ud.fullmirror, d) | 361 | runfetchcmd("touch %s.done" % ud.fullmirror, d) |
297 | 362 | ||
363 | def clone_shallow_local(self, ud, dest, d): | ||
364 | """Clone the repo and make it shallow. | ||
365 | |||
366 | The upstream url of the new clone isn't set at this time, as it'll be | ||
367 | set correctly when unpacked.""" | ||
368 | runfetchcmd("%s clone %s %s %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, dest), d) | ||
369 | |||
370 | to_parse, shallow_branches = [], [] | ||
371 | for name in ud.names: | ||
372 | revision = ud.revisions[name] | ||
373 | to_parse.append('%s~%d^{}' % (revision, ud.shallow_depth - 1)) | ||
374 | |||
375 | # For nobranch, we need a ref, otherwise the commits will be | ||
376 | # removed, and for non-nobranch, we truncate the branch to our | ||
377 | # srcrev, to avoid keeping unnecessary history beyond that. | ||
378 | branch = ud.branches[name] | ||
379 | if ud.nobranch: | ||
380 | ref = "refs/shallow/%s" % name | ||
381 | elif ud.bareclone: | ||
382 | ref = "refs/heads/%s" % branch | ||
383 | else: | ||
384 | ref = "refs/remotes/origin/%s" % branch | ||
385 | |||
386 | shallow_branches.append(ref) | ||
387 | runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest) | ||
388 | |||
389 | # Map srcrev+depths to revisions | ||
390 | shallow_revisions = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join(to_parse)), d, workdir=dest).splitlines() | ||
391 | |||
392 | # Make the repository shallow | ||
393 | shallow_cmd = ['git', 'make-shallow', '-s'] | ||
394 | for b in shallow_branches: | ||
395 | shallow_cmd.append('-r') | ||
396 | shallow_cmd.append(b) | ||
397 | shallow_cmd.extend(shallow_revisions) | ||
398 | runfetchcmd(subprocess.list2cmdline(shallow_cmd), d, workdir=dest) | ||
399 | |||
298 | def unpack(self, ud, destdir, d): | 400 | def unpack(self, ud, destdir, d): |
299 | """ unpack the downloaded src to destdir""" | 401 | """ unpack the downloaded src to destdir""" |
300 | 402 | ||
@@ -311,11 +413,12 @@ class Git(FetchMethod): | |||
311 | if os.path.exists(destdir): | 413 | if os.path.exists(destdir): |
312 | bb.utils.prunedir(destdir) | 414 | bb.utils.prunedir(destdir) |
313 | 415 | ||
314 | cloneflags = "-s -n" | 416 | if ud.shallow and (not os.path.exists(ud.clonedir) or self.need_update(ud, d)): |
315 | if ud.bareclone: | 417 | bb.utils.mkdirhier(destdir) |
316 | cloneflags += " --mirror" | 418 | runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=destdir) |
419 | else: | ||
420 | runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, destdir), d) | ||
317 | 421 | ||
318 | runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, cloneflags, ud.clonedir, destdir), d) | ||
319 | repourl = self._get_repo_url(ud) | 422 | repourl = self._get_repo_url(ud) |
320 | runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, repourl), d, workdir=destdir) | 423 | runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, repourl), d, workdir=destdir) |
321 | if not ud.nocheckout: | 424 | if not ud.nocheckout: |