| Index: checkout.py
|
| diff --git a/checkout.py b/checkout.py
|
| index 8f7c4be2baa44bdaf5c739c891b308ad7ad871ec..7c497612e16d5e9e6cb4f9807f4972a8c6a78c4b 100644
|
| --- a/checkout.py
|
| +++ b/checkout.py
|
| @@ -4,7 +4,7 @@
|
| # found in the LICENSE file.
|
| """Manages a project checkout.
|
|
|
| -Includes support for svn, git-svn and git.
|
| +Includes support only for git.
|
| """
|
|
|
| import fnmatch
|
| @@ -161,403 +161,6 @@ class CheckoutBase(object):
|
| raise NotImplementedError()
|
|
|
|
|
| -class RawCheckout(CheckoutBase):
|
| - """Used to apply a patch locally without any intent to commit it.
|
| -
|
| - To be used by the try server.
|
| - """
|
| - def prepare(self, revision):
|
| - """Stubbed out."""
|
| - pass
|
| -
|
| - def apply_patch(self, patches, post_processors=None, verbose=False):
|
| - """Ignores svn properties."""
|
| - post_processors = post_processors or self.post_processors or []
|
| - for p in patches:
|
| - stdout = []
|
| - try:
|
| - filepath = os.path.join(self.project_path, p.filename)
|
| - if p.is_delete:
|
| - os.remove(filepath)
|
| - assert(not os.path.exists(filepath))
|
| - stdout.append('Deleted.')
|
| - else:
|
| - dirname = os.path.dirname(p.filename)
|
| - full_dir = os.path.join(self.project_path, dirname)
|
| - if dirname and not os.path.isdir(full_dir):
|
| - os.makedirs(full_dir)
|
| - stdout.append('Created missing directory %s.' % dirname)
|
| -
|
| - if p.is_binary:
|
| - content = p.get()
|
| - with open(filepath, 'wb') as f:
|
| - f.write(content)
|
| - stdout.append('Added binary file %d bytes.' % len(content))
|
| - else:
|
| - if p.source_filename:
|
| - if not p.is_new:
|
| - raise PatchApplicationFailed(
|
| - p,
|
| - 'File has a source filename specified but is not new')
|
| - # Copy the file first.
|
| - if os.path.isfile(filepath):
|
| - raise PatchApplicationFailed(
|
| - p, 'File exist but was about to be overwriten')
|
| - shutil.copy2(
|
| - os.path.join(self.project_path, p.source_filename), filepath)
|
| - stdout.append('Copied %s -> %s' % (p.source_filename, p.filename))
|
| - if p.diff_hunks:
|
| - cmd = ['patch', '-u', '--binary', '-p%s' % p.patchlevel]
|
| - if verbose:
|
| - cmd.append('--verbose')
|
| - env = os.environ.copy()
|
| - env['TMPDIR'] = tempfile.mkdtemp(prefix='crpatch')
|
| - try:
|
| - stdout.append(
|
| - subprocess2.check_output(
|
| - cmd,
|
| - stdin=p.get(False),
|
| - stderr=subprocess2.STDOUT,
|
| - cwd=self.project_path,
|
| - timeout=GLOBAL_TIMEOUT,
|
| - env=env))
|
| - finally:
|
| - shutil.rmtree(env['TMPDIR'])
|
| - elif p.is_new and not os.path.exists(filepath):
|
| - # There is only a header. Just create the file.
|
| - open(filepath, 'w').close()
|
| - stdout.append('Created an empty file.')
|
| - for post in post_processors:
|
| - post(self, p)
|
| - if verbose:
|
| - print p.filename
|
| - print align_stdout(stdout)
|
| - except OSError, e:
|
| - raise PatchApplicationFailed(p, '%s%s' % (align_stdout(stdout), e))
|
| - except subprocess.CalledProcessError, e:
|
| - raise PatchApplicationFailed(
|
| - p,
|
| - 'While running %s;\n%s%s' % (
|
| - ' '.join(e.cmd),
|
| - align_stdout(stdout),
|
| - align_stdout([getattr(e, 'stdout', '')])))
|
| -
|
| - def commit(self, commit_message, user):
|
| - """Stubbed out."""
|
| - raise NotImplementedError('RawCheckout can\'t commit')
|
| -
|
| - def revisions(self, _rev1, _rev2):
|
| - return None
|
| -
|
| -
|
| -class SvnConfig(object):
|
| - """Parses a svn configuration file."""
|
| - def __init__(self, svn_config_dir=None):
|
| - super(SvnConfig, self).__init__()
|
| - self.svn_config_dir = svn_config_dir
|
| - self.default = not bool(self.svn_config_dir)
|
| - if not self.svn_config_dir:
|
| - if sys.platform == 'win32':
|
| - self.svn_config_dir = os.path.join(os.environ['APPDATA'], 'Subversion')
|
| - else:
|
| - self.svn_config_dir = os.path.expanduser(
|
| - os.path.join('~', '.subversion'))
|
| - svn_config_file = os.path.join(self.svn_config_dir, 'config')
|
| - parser = configparser.SafeConfigParser()
|
| - if os.path.isfile(svn_config_file):
|
| - parser.read(svn_config_file)
|
| - else:
|
| - parser.add_section('auto-props')
|
| - self.auto_props = dict(parser.items('auto-props'))
|
| -
|
| -
|
| -class SvnMixIn(object):
|
| - """MixIn class to add svn commands common to both svn and git-svn clients."""
|
| - # These members need to be set by the subclass.
|
| - commit_user = None
|
| - commit_pwd = None
|
| - svn_url = None
|
| - project_path = None
|
| - # Override at class level when necessary. If used, --non-interactive is
|
| - # implied.
|
| - svn_config = SvnConfig()
|
| - # Set to True when non-interactivity is necessary but a custom subversion
|
| - # configuration directory is not necessary.
|
| - non_interactive = False
|
| -
|
| - def _add_svn_flags(self, args, non_interactive, credentials=True):
|
| - args = ['svn'] + args
|
| - if not self.svn_config.default:
|
| - args.extend(['--config-dir', self.svn_config.svn_config_dir])
|
| - if not self.svn_config.default or self.non_interactive or non_interactive:
|
| - args.append('--non-interactive')
|
| - if credentials:
|
| - if self.commit_user:
|
| - args.extend(['--username', self.commit_user])
|
| - if self.commit_pwd:
|
| - args.extend(['--password', self.commit_pwd])
|
| - return args
|
| -
|
| - def _check_call_svn(self, args, **kwargs):
|
| - """Runs svn and throws an exception if the command failed."""
|
| - kwargs.setdefault('cwd', self.project_path)
|
| - kwargs.setdefault('stdout', self.VOID)
|
| - kwargs.setdefault('timeout', GLOBAL_TIMEOUT)
|
| - return subprocess2.check_call_out(
|
| - self._add_svn_flags(args, False), **kwargs)
|
| -
|
| - def _check_output_svn(self, args, credentials=True, **kwargs):
|
| - """Runs svn and throws an exception if the command failed.
|
| -
|
| - Returns the output.
|
| - """
|
| - kwargs.setdefault('cwd', self.project_path)
|
| - return subprocess2.check_output(
|
| - self._add_svn_flags(args, True, credentials),
|
| - stderr=subprocess2.STDOUT,
|
| - timeout=GLOBAL_TIMEOUT,
|
| - **kwargs)
|
| -
|
| - @staticmethod
|
| - def _parse_svn_info(output, key):
|
| - """Returns value for key from svn info output.
|
| -
|
| - Case insensitive.
|
| - """
|
| - values = {}
|
| - key = key.lower()
|
| - for line in output.splitlines(False):
|
| - if not line:
|
| - continue
|
| - k, v = line.split(':', 1)
|
| - k = k.strip().lower()
|
| - v = v.strip()
|
| - assert not k in values
|
| - values[k] = v
|
| - return values.get(key, None)
|
| -
|
| -
|
| -class SvnCheckout(CheckoutBase, SvnMixIn):
|
| - """Manages a subversion checkout."""
|
| - def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url,
|
| - post_processors=None):
|
| - CheckoutBase.__init__(self, root_dir, project_name, post_processors)
|
| - SvnMixIn.__init__(self)
|
| - self.commit_user = commit_user
|
| - self.commit_pwd = commit_pwd
|
| - self.svn_url = svn_url
|
| - assert bool(self.commit_user) >= bool(self.commit_pwd)
|
| -
|
| - def prepare(self, revision):
|
| - # Will checkout if the directory is not present.
|
| - assert self.svn_url
|
| - if not os.path.isdir(self.project_path):
|
| - logging.info('Checking out %s in %s' %
|
| - (self.project_name, self.project_path))
|
| - return self._revert(revision)
|
| -
|
| - def apply_patch(self, patches, post_processors=None, verbose=False):
|
| - post_processors = post_processors or self.post_processors or []
|
| - for p in patches:
|
| - stdout = []
|
| - try:
|
| - filepath = os.path.join(self.project_path, p.filename)
|
| - # It is important to use credentials=False otherwise credentials could
|
| - # leak in the error message. Credentials are not necessary here for the
|
| - # following commands anyway.
|
| - if p.is_delete:
|
| - stdout.append(self._check_output_svn(
|
| - ['delete', p.filename, '--force'], credentials=False))
|
| - assert(not os.path.exists(filepath))
|
| - stdout.append('Deleted.')
|
| - else:
|
| - # svn add while creating directories otherwise svn add on the
|
| - # contained files will silently fail.
|
| - # First, find the root directory that exists.
|
| - dirname = os.path.dirname(p.filename)
|
| - dirs_to_create = []
|
| - while (dirname and
|
| - not os.path.isdir(os.path.join(self.project_path, dirname))):
|
| - dirs_to_create.append(dirname)
|
| - dirname = os.path.dirname(dirname)
|
| - for dir_to_create in reversed(dirs_to_create):
|
| - os.mkdir(os.path.join(self.project_path, dir_to_create))
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['add', dir_to_create, '--force'], credentials=False))
|
| - stdout.append('Created missing directory %s.' % dir_to_create)
|
| -
|
| - if p.is_binary:
|
| - content = p.get()
|
| - with open(filepath, 'wb') as f:
|
| - f.write(content)
|
| - stdout.append('Added binary file %d bytes.' % len(content))
|
| - else:
|
| - if p.source_filename:
|
| - if not p.is_new:
|
| - raise PatchApplicationFailed(
|
| - p,
|
| - 'File has a source filename specified but is not new')
|
| - # Copy the file first.
|
| - if os.path.isfile(filepath):
|
| - raise PatchApplicationFailed(
|
| - p, 'File exist but was about to be overwriten')
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['copy', p.source_filename, p.filename]))
|
| - stdout.append('Copied %s -> %s' % (p.source_filename, p.filename))
|
| - if p.diff_hunks:
|
| - cmd = [
|
| - 'patch',
|
| - '-p%s' % p.patchlevel,
|
| - '--forward',
|
| - '--force',
|
| - '--no-backup-if-mismatch',
|
| - ]
|
| - env = os.environ.copy()
|
| - env['TMPDIR'] = tempfile.mkdtemp(prefix='crpatch')
|
| - try:
|
| - stdout.append(
|
| - subprocess2.check_output(
|
| - cmd,
|
| - stdin=p.get(False),
|
| - cwd=self.project_path,
|
| - timeout=GLOBAL_TIMEOUT,
|
| - env=env))
|
| - finally:
|
| - shutil.rmtree(env['TMPDIR'])
|
| -
|
| - elif p.is_new and not os.path.exists(filepath):
|
| - # There is only a header. Just create the file if it doesn't
|
| - # exist.
|
| - open(filepath, 'w').close()
|
| - stdout.append('Created an empty file.')
|
| - if p.is_new and not p.source_filename:
|
| - # Do not run it if p.source_filename is defined, since svn copy was
|
| - # using above.
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['add', p.filename, '--force'], credentials=False))
|
| - for name, value in p.svn_properties:
|
| - if value is None:
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['propdel', '--quiet', name, p.filename],
|
| - credentials=False))
|
| - stdout.append('Property %s deleted.' % name)
|
| - else:
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['propset', name, value, p.filename], credentials=False))
|
| - stdout.append('Property %s=%s' % (name, value))
|
| - for prop, values in self.svn_config.auto_props.iteritems():
|
| - if fnmatch.fnmatch(p.filename, prop):
|
| - for value in values.split(';'):
|
| - if '=' not in value:
|
| - params = [value, '.']
|
| - else:
|
| - params = value.split('=', 1)
|
| - if params[1] == '*':
|
| - # Works around crbug.com/150960 on Windows.
|
| - params[1] = '.'
|
| - stdout.append(
|
| - self._check_output_svn(
|
| - ['propset'] + params + [p.filename], credentials=False))
|
| - stdout.append('Property (auto) %s' % '='.join(params))
|
| - for post in post_processors:
|
| - post(self, p)
|
| - if verbose:
|
| - print p.filename
|
| - print align_stdout(stdout)
|
| - except OSError, e:
|
| - raise PatchApplicationFailed(p, '%s%s' % (align_stdout(stdout), e))
|
| - except subprocess.CalledProcessError, e:
|
| - raise PatchApplicationFailed(
|
| - p,
|
| - 'While running %s;\n%s%s' % (
|
| - ' '.join(e.cmd),
|
| - align_stdout(stdout),
|
| - align_stdout([getattr(e, 'stdout', '')])))
|
| -
|
| - def commit(self, commit_message, user):
|
| - logging.info('Committing patch for %s' % user)
|
| - assert self.commit_user
|
| - assert isinstance(commit_message, unicode)
|
| - handle, commit_filename = tempfile.mkstemp(text=True)
|
| - try:
|
| - # Shouldn't assume default encoding is UTF-8. But really, if you are using
|
| - # anything else, you are living in another world.
|
| - os.write(handle, commit_message.encode('utf-8'))
|
| - os.close(handle)
|
| - # When committing, svn won't update the Revision metadata of the checkout,
|
| - # so if svn commit returns "Committed revision 3.", svn info will still
|
| - # return "Revision: 2". Since running svn update right after svn commit
|
| - # creates a race condition with other committers, this code _must_ parse
|
| - # the output of svn commit and use a regexp to grab the revision number.
|
| - # Note that "Committed revision N." is localized but subprocess2 forces
|
| - # LANGUAGE=en.
|
| - args = ['commit', '--file', commit_filename]
|
| - # realauthor is parsed by a server-side hook.
|
| - if user and user != self.commit_user:
|
| - args.extend(['--with-revprop', 'realauthor=%s' % user])
|
| - out = self._check_output_svn(args)
|
| - finally:
|
| - os.remove(commit_filename)
|
| - lines = filter(None, out.splitlines())
|
| - match = re.match(r'^Committed revision (\d+).$', lines[-1])
|
| - if not match:
|
| - raise PatchApplicationFailed(
|
| - None,
|
| - 'Couldn\'t make sense out of svn commit message:\n' + out)
|
| - return int(match.group(1))
|
| -
|
| - def _revert(self, revision):
|
| - """Reverts local modifications or checks out if the directory is not
|
| - present. Use depot_tools's functionality to do this.
|
| - """
|
| - flags = ['--ignore-externals']
|
| - if revision:
|
| - flags.extend(['--revision', str(revision)])
|
| - if os.path.isdir(self.project_path):
|
| - # This may remove any part (or all) of the checkout.
|
| - scm.SVN.Revert(self.project_path, no_ignore=True)
|
| -
|
| - if os.path.isdir(self.project_path):
|
| - # Revive files that were deleted in scm.SVN.Revert().
|
| - self._check_call_svn(['update', '--force'] + flags,
|
| - timeout=FETCH_TIMEOUT)
|
| - else:
|
| - logging.info(
|
| - 'Directory %s is not present, checking it out.' % self.project_path)
|
| - self._check_call_svn(
|
| - ['checkout', self.svn_url, self.project_path] + flags, cwd=None,
|
| - timeout=FETCH_TIMEOUT)
|
| - return self._get_revision()
|
| -
|
| - def _get_revision(self):
|
| - out = self._check_output_svn(['info', '.'])
|
| - revision = int(self._parse_svn_info(out, 'revision'))
|
| - if revision != self._last_seen_revision:
|
| - logging.info('Updated to revision %d' % revision)
|
| - self._last_seen_revision = revision
|
| - return revision
|
| -
|
| - def revisions(self, rev1, rev2):
|
| - """Returns the number of actual commits, not just the difference between
|
| - numbers.
|
| - """
|
| - rev2 = rev2 or 'HEAD'
|
| - # Revision range is inclusive and ordering doesn't matter, they'll appear in
|
| - # the order specified.
|
| - try:
|
| - out = self._check_output_svn(
|
| - ['log', '-q', self.svn_url, '-r', '%s:%s' % (rev1, rev2)])
|
| - except subprocess.CalledProcessError:
|
| - return None
|
| - # Ignore the '----' lines.
|
| - return len([l for l in out.splitlines() if l.startswith('r')]) - 1
|
| -
|
| -
|
| class GitCheckout(CheckoutBase):
|
| """Manages a git checkout."""
|
| def __init__(self, root_dir, project_name, remote_branch, git_url,
|
| @@ -639,8 +242,6 @@ class GitCheckout(CheckoutBase):
|
| """Applies a patch on 'working_branch' and switches to it.
|
|
|
| The changes remain staged on the current branch.
|
| -
|
| - Ignores svn properties and raise an exception on unexpected ones.
|
| """
|
| post_processors = post_processors or self.post_processors or []
|
| # It this throws, the checkout is corrupted. Maybe worth deleting it and
|
| @@ -686,19 +287,6 @@ class GitCheckout(CheckoutBase):
|
| if verbose:
|
| cmd.append('--verbose')
|
| stdout.append(self._check_output_git(cmd, stdin=p.get(True)))
|
| - for key, value in p.svn_properties:
|
| - # Ignore some known auto-props flags through .subversion/config,
|
| - # bails out on the other ones.
|
| - # TODO(maruel): Read ~/.subversion/config and detect the rules that
|
| - # applies here to figure out if the property will be correctly
|
| - # handled.
|
| - stdout.append('Property %s=%s' % (key, value))
|
| - if not key in (
|
| - 'svn:eol-style', 'svn:executable', 'svn:mime-type'):
|
| - raise patch.UnsupportedPatchFormat(
|
| - p.filename,
|
| - 'Cannot apply svn property %s to file %s.' % (
|
| - key, p.filename))
|
| for post in post_processors:
|
| post(self, p)
|
| if verbose:
|
|
|