1# Copyright (c) Facebook, Inc. and its affiliates. 2# 3# This source code is licensed under the MIT license found in the 4# LICENSE file in the root directory of this source tree. 5 6from __future__ import absolute_import, division, print_function, unicode_literals 7 8import os 9import shutil 10import subprocess 11 12from .platform import is_windows 13 14 15PREFETCHED_DIRS = set() 16 17 18def containing_repo_type(path): 19 while True: 20 if os.path.exists(os.path.join(path, ".git")): 21 return ("git", path) 22 if os.path.exists(os.path.join(path, ".hg")): 23 return ("hg", path) 24 25 parent = os.path.dirname(path) 26 if parent == path: 27 return None, None 28 path = parent 29 30 31def find_eden_root(dirpath): 32 """If the specified directory is inside an EdenFS checkout, returns 33 the canonical absolute path to the root of that checkout. 34 35 Returns None if the specified directory is not in an EdenFS checkout. 36 """ 37 if is_windows(): 38 repo_type, repo_root = containing_repo_type(dirpath) 39 if repo_root is not None: 40 if os.path.exists(os.path.join(repo_root, ".eden", "config")): 41 return os.path.realpath(repo_root) 42 return None 43 44 try: 45 return os.readlink(os.path.join(dirpath, ".eden", "root")) 46 except OSError: 47 return None 48 49 50def prefetch_dir_if_eden(dirpath): 51 """After an amend/rebase, Eden may need to fetch a large number 52 of trees from the servers. The simplistic single threaded walk 53 performed by copytree makes this more expensive than is desirable 54 so we help accelerate things by performing a prefetch on the 55 source directory""" 56 global PREFETCHED_DIRS 57 if dirpath in PREFETCHED_DIRS: 58 return 59 root = find_eden_root(dirpath) 60 if root is None: 61 return 62 glob = f"{os.path.relpath(dirpath, root).replace(os.sep, '/')}/**" 63 print(f"Prefetching {glob}") 64 subprocess.call(["edenfsctl", "prefetch", "--repo", root, "--silent", glob]) 65 PREFETCHED_DIRS.add(dirpath) 66 67 68def copytree(src_dir, dest_dir, ignore=None): 69 """Recursively copy the src_dir to the dest_dir, filtering 70 out entries using the ignore lambda. The behavior of the 71 ignore lambda must match that described by `shutil.copytree`. 72 This `copytree` function knows how to prefetch data when 73 running in an eden repo. 74 TODO: I'd like to either extend this or add a variant that 75 uses watchman to mirror src_dir into dest_dir. 76 """ 77 prefetch_dir_if_eden(src_dir) 78 return shutil.copytree(src_dir, dest_dir, ignore=ignore) 79