diff options
Diffstat (limited to 'utils/git-sync-deps')
-rwxr-xr-x | utils/git-sync-deps | 71 |
1 files changed, 55 insertions, 16 deletions
diff --git a/utils/git-sync-deps b/utils/git-sync-deps index 7a7e606f..43548fe4 100755 --- a/utils/git-sync-deps +++ b/utils/git-sync-deps @@ -30,6 +30,13 @@ """Parse a DEPS file and git checkout all of the dependencies. Args: + --treeless Clone repos without trees. This is the fast option, useful + when you only need a single commit, like on a build machine. + Defers getting objects until checkout time. + Otherwise clones without blobs. + Requires git 2.20 or later. + https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ + An optional list of deps_os values. Environment Variables: @@ -59,12 +66,14 @@ import sys import threading from builtins import bytes - def git_executable(): """Find the git executable. Returns: - A string suitable for passing to subprocess functions, or None. + A triple: + A string suitable for passing to subprocess functions, or None. + The major version number + The minor version number """ envgit = os.environ.get('GIT_EXECUTABLE') searchlist = ['git', 'git.exe', 'git.bat'] @@ -72,12 +81,21 @@ def git_executable(): searchlist.insert(0, envgit) with open(os.devnull, 'w') as devnull: for git in searchlist: + major=None + minor=None try: - subprocess.call([git, '--version'], stdout=devnull) + version_info = subprocess.check_output([git, '--version']).decode('utf-8') + match = re.search("^git version (\d+)\.(\d+)",version_info) + print("Using {}".format(version_info)) + if match: + major = int(match.group(1)) + minor = int(match.group(2)) + else: + continue except (OSError,): continue - return git - return None + return (git,major,minor) + return (None,0,0) DEFAULT_DEPS_PATH = os.path.normpath( @@ -97,6 +115,9 @@ def usage(deps_file_path = None): sys.stderr.write(__doc__) +def looks_like_raw_commit(commit): + return re.match('^[a-f0-9]{40}$', commit) is not None + def git_repository_sync_is_disabled(git, directory): try: disable = subprocess.check_output( @@ -125,14 +146,14 @@ def is_git_toplevel(git, directory): def status(directory, checkoutable): def truncate(s, length): - return s if len(s) <= length else s[:(length - 3)] + '...' + return s if len(s) <= length else '...' + s[-(length - 3):] dlen = 36 directory = truncate(directory, dlen) checkoutable = truncate(checkoutable, 40) sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable)) -def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): +def git_checkout_to_directory(git, repo, checkoutable, directory, verbose, treeless): """Checkout (and clone if needed) a Git repository. Args: @@ -147,13 +168,22 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): directory (string) the path into which the repository should be checked out. - verbose (boolean) + verbose (boolean): emit status info to stdout + + treeless (boolean): when true, clone without any trees. Raises an exception if any calls to git fail. """ if not os.path.isdir(directory): + # Use blobless or treeless checkouts for faster downloads. + # This defers some work to checkout time. + # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ + filter = ['--filter=tree:0'] if treeless else ['--filter=blob:none'] + # If the thing to check out looks like a tag (and not like a commit), + # then limit the checkout to that branch. + branch = [] if looks_like_raw_commit(checkoutable) else ['--branch={}'.format(checkoutable)] subprocess.check_call( - [git, 'clone', '--quiet', repo, directory]) + [git, 'clone', '--quiet', '--single-branch'] + filter + branch + [repo, directory]) if not is_git_toplevel(git, directory): # if the directory exists, but isn't a git repo, you will modify @@ -200,7 +230,7 @@ def parse_file_to_dict(path): return dictionary -def git_sync_deps(deps_file_path, command_line_os_requests, verbose): +def git_sync_deps(deps_file_path, command_line_os_requests, verbose, treeless): """Grab dependencies, with optional platform support. Args: @@ -210,11 +240,20 @@ def git_sync_deps(deps_file_path, command_line_os_requests, verbose): List of strings that should each be a key in the deps_os dictionary in the DEPS file. + verbose (boolean): emit status info to stdout + + treeless (boolean): when true, clone as treeless instead of blobless + Raises git Exceptions. """ - git = git_executable() + (git,git_major,git_minor) = git_executable() assert git + # --filter=tree:0 is available in git 2.20 and later + if (git_major,git_minor) < (2,20): + print("disabling --treeless: git is older than v2.20") + treeless = False + deps_file_directory = os.path.dirname(deps_file_path) deps_file = parse_file_to_dict(deps_file_path) dependencies = deps_file['deps'].copy() @@ -232,6 +271,7 @@ def git_sync_deps(deps_file_path, command_line_os_requests, verbose): if directory.startswith(other_dir + '/'): raise Exception('%r is parent of %r' % (other_dir, directory)) list_of_arg_lists = [] + print("deps {}".format(dependencies)) for directory in sorted(dependencies): if '@' in dependencies[directory]: repo, checkoutable = dependencies[directory].split('@', 1) @@ -241,7 +281,7 @@ def git_sync_deps(deps_file_path, command_line_os_requests, verbose): relative_directory = os.path.join(deps_file_directory, directory) list_of_arg_lists.append( - (git, repo, checkoutable, relative_directory, verbose)) + (git, repo, checkoutable, relative_directory, verbose, treeless)) multithread(git_checkout_to_directory, list_of_arg_lists) @@ -266,15 +306,14 @@ def multithread(function, list_of_arg_lists): def main(argv): deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH) verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)) + treeless = bool("--treeless" in argv) + argv = [x for x in argv if x != "--treeless"] if '--help' in argv or '-h' in argv: usage(deps_file_path) return 1 - git_sync_deps(deps_file_path, argv, verbose) - # subprocess.check_call( - # [sys.executable, - # os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')]) + git_sync_deps(deps_file_path, argv, verbose, treeless) return 0 |