Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2756)

Unified Diff: checkout.py

Issue 2398603003: Remove SVN support from checkout.py (Closed)
Patch Set: Updated patchset dependency Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « apply_issue.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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:
« no previous file with comments | « apply_issue.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698