gclient-new-workdir.py 4.04 KB
Newer Older
1 2 3 4 5 6
#!/usr/bin/env python
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Usage:
7
#    gclient-new-workdir.py [options] <repository> <new_workdir>
8 9
#

10
import argparse
11 12 13 14
import os
import shutil
import subprocess
import sys
15
import textwrap
16

sammc@chromium.org's avatar
sammc@chromium.org committed
17 18
import git_common

19

20 21
def parse_options():
  if sys.platform == 'win32':
22 23
    print('ERROR: This script cannot run on Windows because it uses symlinks.')
    sys.exit(1)
24

25 26 27 28 29 30
  parser = argparse.ArgumentParser(description='''\
      Clone an existing gclient directory, taking care of all sub-repositories.
      Works similarly to 'git new-workdir'.''')
  parser.add_argument('repository', type=os.path.abspath,
                      help='should contain a .gclient file')
  parser.add_argument('new_workdir', help='must not exist')
31 32 33 34 35 36
  parser.add_argument('--reflink', action='store_true', default=None,
                      help='''force to use "cp --reflink" for speed and disk
                              space. need supported FS like btrfs or ZFS.''')
  parser.add_argument('--no-reflink', action='store_false', dest='reflink',
                      help='''force not to use "cp --reflink" even on supported
                              FS like btrfs or ZFS.''')
37
  args = parser.parse_args()
38

39 40
  if not os.path.exists(args.repository):
    parser.error('Repository "%s" does not exist.' % args.repository)
41

42 43 44
  gclient = os.path.join(args.repository, '.gclient')
  if not os.path.exists(gclient):
    parser.error('No .gclient file at "%s".' % gclient)
45

46 47
  if os.path.exists(args.new_workdir):
    parser.error('New workdir "%s" already exists.' % args.new_workdir)
48

49
  return args
50 51


52
def support_cow(src, dest):
53 54
  # 'cp --reflink' always succeeds when 'src' is a symlink or a directory
  assert os.path.isfile(src) and not os.path.islink(src)
55 56 57 58 59 60
  try:
    subprocess.check_output(['cp', '-a', '--reflink', src, dest],
                            stderr=subprocess.STDOUT)
  except subprocess.CalledProcessError:
    return False
  finally:
61 62
    if os.path.isfile(dest):
      os.remove(dest)
63 64 65
  return True


66 67 68 69
def try_vol_snapshot(src, dest):
  try:
    subprocess.check_call(['btrfs', 'subvol', 'snapshot', src, dest],
                            stderr=subprocess.STDOUT)
70
  except (subprocess.CalledProcessError, OSError):
71 72 73 74
    return False
  return True


75
def main():
76
  args = parse_options()
77

78
  gclient = os.path.join(args.repository, '.gclient')
79
  if os.path.islink(gclient):
80
    gclient = os.path.realpath(gclient)
81
  new_gclient = os.path.join(args.new_workdir, '.gclient')
82

83 84 85 86 87 88 89 90 91
  if try_vol_snapshot(args.repository, args.new_workdir):
    args.reflink = True
  else:
    os.makedirs(args.new_workdir)
    if args.reflink is None:
      args.reflink = support_cow(gclient, new_gclient)
      if args.reflink:
        print('Copy-on-write support is detected.')
    os.symlink(gclient, new_gclient)
92

93
  for root, dirs, _ in os.walk(args.repository):
94
    if '.git' in dirs:
95
      workdir = root.replace(args.repository, args.new_workdir, 1)
sammc@chromium.org's avatar
sammc@chromium.org committed
96
      print('Creating: %s' % workdir)
97 98 99 100 101 102 103

      if args.reflink:
        if not os.path.exists(workdir):
          print('Copying: %s' % workdir)
          subprocess.check_call(['cp', '-a', '--reflink', root, workdir])
        shutil.rmtree(os.path.join(workdir, '.git'))

sammc@chromium.org's avatar
sammc@chromium.org committed
104 105
      git_common.make_workdir(os.path.join(root, '.git'),
                              os.path.join(workdir, '.git'))
106 107 108 109 110 111 112
      if args.reflink:
        subprocess.check_call(['cp', '-a', '--reflink',
                              os.path.join(root, '.git', 'index'),
                              os.path.join(workdir, '.git', 'index')])
      else:
        subprocess.check_call(['git', 'checkout', '-f'], cwd=workdir)

113 114 115 116
  if args.reflink:
    print(textwrap.dedent('''\
      The repo was copied with copy-on-write, and the artifacts were retained.
      More details on http://crbug.com/721585.
117

118 119
      Depending on your usage pattern, you might want to do "gn gen"
      on the output directories. More details: http://crbug.com/723856.'''))
120 121

if __name__ == '__main__':
122
  sys.exit(main())