summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Koch <stefan-koch@siemens.com>2025-03-03 14:49:09 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2025-03-06 11:16:46 +0000
commit65ae50cd16a3989699bea845566d38476f2ae9a7 (patch)
tree0f40d6776a50b169e641331884f07195749e5c1c
parent9be2fc0031b0c9eaee786a4f5f50600ad7dfa7e0 (diff)
downloadpoky-65ae50cd16a3989699bea845566d38476f2ae9a7.tar.gz
bitbake: fetch2/git: Add support for fast initial shallow fetch
When `ud.shallow == 1`: - Prefer an initial shallow clone over an initial full bare clone, while still utilizing any already existing full bare clones. - If the Git error "Server does not allow request for unadvertised object" occurs, the initial full bare clone is fetched automatically. This may happen if the Git server does not allow the request or if the Git client has issues with this functionality, especially with the Git client from Ubuntu 20.04. This improves: - Resolve timeout issues during initial clones on slow internet connections by reducing the amount of data transferred. - Eliminate the need to use an HTTPS tarball `SRC_URI` to reduce data transfer. - Allow SSH-based authentication (e.g. cert and agent-based) when using non-public repos, so additional HTTPS tokens may not be required. (Bitbake rev: 457288b2fda86fd00cdcaefac616129b0029e1f9) Signed-off-by: Stefan Koch <stefan-koch@siemens.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/bb/fetch2/git.py114
1 files changed, 85 insertions, 29 deletions
diff --git a/bitbake/lib/bb/fetch2/git.py b/bitbake/lib/bb/fetch2/git.py
index 168f14d0c8..9a15abaa79 100644
--- a/bitbake/lib/bb/fetch2/git.py
+++ b/bitbake/lib/bb/fetch2/git.py
@@ -207,6 +207,7 @@ class Git(FetchMethod):
207 if ud.bareclone: 207 if ud.bareclone:
208 ud.cloneflags += " --mirror" 208 ud.cloneflags += " --mirror"
209 209
210 ud.shallow_skip_fast = False
210 ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1" 211 ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1"
211 ud.shallow_extra_refs = (d.getVar("BB_GIT_SHALLOW_EXTRA_REFS") or "").split() 212 ud.shallow_extra_refs = (d.getVar("BB_GIT_SHALLOW_EXTRA_REFS") or "").split()
212 213
@@ -446,6 +447,24 @@ class Git(FetchMethod):
446 if ud.proto.lower() != 'file': 447 if ud.proto.lower() != 'file':
447 bb.fetch2.check_network_access(d, clone_cmd, ud.url) 448 bb.fetch2.check_network_access(d, clone_cmd, ud.url)
448 progresshandler = GitProgressHandler(d) 449 progresshandler = GitProgressHandler(d)
450
451 # Try creating a fast initial shallow clone
452 # Enabling ud.shallow_skip_fast will skip this
453 # If the Git error "Server does not allow request for unadvertised object"
454 # occurs, shallow_skip_fast is enabled automatically.
455 # This may happen if the Git server does not allow the request
456 # or if the Git client has issues with this functionality.
457 if ud.shallow and not ud.shallow_skip_fast:
458 try:
459 self.clone_shallow_with_tarball(ud, d)
460 # When the shallow clone has succeeded, use the shallow tarball
461 ud.localpath = ud.fullshallow
462 return
463 except:
464 logger.warning("Creating fast initial shallow clone failed, try initial regular clone now.")
465
466 # When skipping fast initial shallow or the fast inital shallow clone failed:
467 # Try again with an initial regular clone
449 runfetchcmd(clone_cmd, d, log=progresshandler) 468 runfetchcmd(clone_cmd, d, log=progresshandler)
450 469
451 # Update the checkout if needed 470 # Update the checkout if needed
@@ -508,48 +527,74 @@ class Git(FetchMethod):
508 if os.path.exists(os.path.join(ud.destdir, ".git", "lfs")): 527 if os.path.exists(os.path.join(ud.destdir, ".git", "lfs")):
509 runfetchcmd("tar -cf - lfs | tar -xf - -C %s" % ud.clonedir, d, workdir="%s/.git" % ud.destdir) 528 runfetchcmd("tar -cf - lfs | tar -xf - -C %s" % ud.clonedir, d, workdir="%s/.git" % ud.destdir)
510 529
511 def build_mirror_data(self, ud, d): 530 def lfs_fetch(self, ud, d, clonedir, revision, fetchall=False, progresshandler=None):
512 531 """Helper method for fetching Git LFS data"""
513 # Create as a temp file and move atomically into position to avoid races 532 try:
514 @contextmanager 533 if self._need_lfs(ud) and self._contains_lfs(ud, d, clonedir) and self._find_git_lfs(d) and len(revision):
515 def create_atomic(filename): 534 # Using worktree with the revision because .lfsconfig may exists
516 fd, tfile = tempfile.mkstemp(dir=os.path.dirname(filename)) 535 worktree_add_cmd = "%s worktree add wt %s" % (ud.basecmd, revision)
517 try: 536 runfetchcmd(worktree_add_cmd, d, log=progresshandler, workdir=clonedir)
518 yield tfile 537 lfs_fetch_cmd = "%s lfs fetch %s" % (ud.basecmd, "--all" if fetchall else "")
519 umask = os.umask(0o666) 538 runfetchcmd(lfs_fetch_cmd, d, log=progresshandler, workdir=(clonedir + "/wt"))
520 os.umask(umask) 539 worktree_rem_cmd = "%s worktree remove -f wt" % ud.basecmd
521 os.chmod(tfile, (0o666 & ~umask)) 540 runfetchcmd(worktree_rem_cmd, d, log=progresshandler, workdir=clonedir)
522 os.rename(tfile, filename) 541 except:
523 finally: 542 logger.warning("Fetching LFS did not succeed.")
524 os.close(fd) 543
544 @contextmanager
545 def create_atomic(self, filename):
546 """Create as a temp file and move atomically into position to avoid races"""
547 fd, tfile = tempfile.mkstemp(dir=os.path.dirname(filename))
548 try:
549 yield tfile
550 umask = os.umask(0o666)
551 os.umask(umask)
552 os.chmod(tfile, (0o666 & ~umask))
553 os.rename(tfile, filename)
554 finally:
555 os.close(fd)
525 556
557 def build_mirror_data(self, ud, d):
526 if ud.shallow and ud.write_shallow_tarballs: 558 if ud.shallow and ud.write_shallow_tarballs:
527 if not os.path.exists(ud.fullshallow): 559 if not os.path.exists(ud.fullshallow):
528 if os.path.islink(ud.fullshallow): 560 if os.path.islink(ud.fullshallow):
529 os.unlink(ud.fullshallow) 561 os.unlink(ud.fullshallow)
530 tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR')) 562 self.clone_shallow_with_tarball(ud, d)
531 shallowclone = os.path.join(tempdir, 'git')
532 try:
533 self.clone_shallow_local(ud, shallowclone, d)
534
535 logger.info("Creating tarball of git repository")
536 with create_atomic(ud.fullshallow) as tfile:
537 runfetchcmd("tar -czf %s ." % tfile, d, workdir=shallowclone)
538 runfetchcmd("touch %s.done" % ud.fullshallow, d)
539 finally:
540 bb.utils.remove(tempdir, recurse=True)
541 elif ud.write_tarballs and not os.path.exists(ud.fullmirror): 563 elif ud.write_tarballs and not os.path.exists(ud.fullmirror):
542 if os.path.islink(ud.fullmirror): 564 if os.path.islink(ud.fullmirror):
543 os.unlink(ud.fullmirror) 565 os.unlink(ud.fullmirror)
544 566
545 logger.info("Creating tarball of git repository") 567 logger.info("Creating tarball of git repository")
546 with create_atomic(ud.fullmirror) as tfile: 568 with self.create_atomic(ud.fullmirror) as tfile:
547 mtime = runfetchcmd("{} log --all -1 --format=%cD".format(ud.basecmd), d, 569 mtime = runfetchcmd("{} log --all -1 --format=%cD".format(ud.basecmd), d,
548 quiet=True, workdir=ud.clonedir) 570 quiet=True, workdir=ud.clonedir)
549 runfetchcmd("tar -czf %s --owner oe:0 --group oe:0 --mtime \"%s\" ." 571 runfetchcmd("tar -czf %s --owner oe:0 --group oe:0 --mtime \"%s\" ."
550 % (tfile, mtime), d, workdir=ud.clonedir) 572 % (tfile, mtime), d, workdir=ud.clonedir)
551 runfetchcmd("touch %s.done" % ud.fullmirror, d) 573 runfetchcmd("touch %s.done" % ud.fullmirror, d)
552 574
575 def clone_shallow_with_tarball(self, ud, d):
576 ret = False
577 tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
578 shallowclone = os.path.join(tempdir, 'git')
579 try:
580 try:
581 self.clone_shallow_local(ud, shallowclone, d)
582 except:
583 logger.warning("Fash shallow clone failed, try to skip fast mode now.")
584 bb.utils.remove(tempdir, recurse=True)
585 os.mkdir(tempdir)
586 ud.shallow_skip_fast = True
587 self.clone_shallow_local(ud, shallowclone, d)
588 logger.info("Creating tarball of git repository")
589 with self.create_atomic(ud.fullshallow) as tfile:
590 runfetchcmd("tar -czf %s ." % tfile, d, workdir=shallowclone)
591 runfetchcmd("touch %s.done" % ud.fullshallow, d)
592 ret = True
593 finally:
594 bb.utils.remove(tempdir, recurse=True)
595
596 return ret
597
553 def clone_shallow_local(self, ud, dest, d): 598 def clone_shallow_local(self, ud, dest, d):
554 """ 599 """
555 Shallow fetch from ud.clonedir (${DL_DIR}/git2/<gitrepo> by default): 600 Shallow fetch from ud.clonedir (${DL_DIR}/git2/<gitrepo> by default):
@@ -557,12 +602,20 @@ class Git(FetchMethod):
557 - For BB_GIT_SHALLOW_REVS: git fetch --shallow-exclude=<revs> rev 602 - For BB_GIT_SHALLOW_REVS: git fetch --shallow-exclude=<revs> rev
558 """ 603 """
559 604
605 progresshandler = GitProgressHandler(d)
606 repourl = self._get_repo_url(ud)
560 bb.utils.mkdirhier(dest) 607 bb.utils.mkdirhier(dest)
561 init_cmd = "%s init -q" % ud.basecmd 608 init_cmd = "%s init -q" % ud.basecmd
562 if ud.bareclone: 609 if ud.bareclone:
563 init_cmd += " --bare" 610 init_cmd += " --bare"
564 runfetchcmd(init_cmd, d, workdir=dest) 611 runfetchcmd(init_cmd, d, workdir=dest)
565 runfetchcmd("%s remote add origin %s" % (ud.basecmd, ud.clonedir), d, workdir=dest) 612 # Use repourl when creating a fast initial shallow clone
613 # Prefer already existing full bare clones if available
614 if not ud.shallow_skip_fast and not os.path.exists(ud.clonedir):
615 remote = shlex.quote(repourl)
616 else:
617 remote = ud.clonedir
618 runfetchcmd("%s remote add origin %s" % (ud.basecmd, remote), d, workdir=dest)
566 619
567 # Check the histories which should be excluded 620 # Check the histories which should be excluded
568 shallow_exclude = '' 621 shallow_exclude = ''
@@ -600,10 +653,14 @@ class Git(FetchMethod):
600 # The ud.clonedir is a local temporary dir, will be removed when 653 # The ud.clonedir is a local temporary dir, will be removed when
601 # fetch is done, so we can do anything on it. 654 # fetch is done, so we can do anything on it.
602 adv_cmd = 'git branch -f advertise-%s %s' % (revision, revision) 655 adv_cmd = 'git branch -f advertise-%s %s' % (revision, revision)
603 runfetchcmd(adv_cmd, d, workdir=ud.clonedir) 656 if ud.shallow_skip_fast:
657 runfetchcmd(adv_cmd, d, workdir=ud.clonedir)
604 658
605 runfetchcmd(fetch_cmd, d, workdir=dest) 659 runfetchcmd(fetch_cmd, d, workdir=dest)
606 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest) 660 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest)
661 # Fetch Git LFS data for fast shallow clones
662 if not ud.shallow_skip_fast:
663 self.lfs_fetch(ud, d, dest, ud.revisions[ud.names[0]])
607 664
608 # Apply extra ref wildcards 665 # Apply extra ref wildcards
609 all_refs_remote = runfetchcmd("%s ls-remote origin 'refs/*'" % ud.basecmd, \ 666 all_refs_remote = runfetchcmd("%s ls-remote origin 'refs/*'" % ud.basecmd, \
@@ -629,7 +686,6 @@ class Git(FetchMethod):
629 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest) 686 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest)
630 687
631 # The url is local ud.clonedir, set it to upstream one 688 # The url is local ud.clonedir, set it to upstream one
632 repourl = self._get_repo_url(ud)
633 runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=dest) 689 runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=dest)
634 690
635 def unpack(self, ud, destdir, d): 691 def unpack(self, ud, destdir, d):