summaryrefslogtreecommitdiff
path: root/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
diff options
context:
space:
mode:
authorKonstantin Tokarev <annulen@yandex.ru>2016-08-25 19:20:41 +0300
committerKonstantin Tokarev <annulen@yandex.ru>2017-02-02 12:30:55 +0000
commit6882a04fb36642862b11efe514251d32070c3d65 (patch)
treeb7959826000b061fd5ccc7512035c7478742f7b0 /Tools/Scripts/webkitpy/common/checkout/scm/svn.py
parentab6df191029eeeb0b0f16f127d553265659f739e (diff)
downloadqtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Tools/Scripts/webkitpy/common/checkout/scm/svn.py')
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/svn.py380
1 files changed, 0 insertions, 380 deletions
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py b/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
deleted file mode 100644
index c146f5d32..000000000
--- a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
+++ /dev/null
@@ -1,380 +0,0 @@
-# Copyright (c) 2009, 2010, 2011 Google Inc. All rights reserved.
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import logging
-import os
-import random
-import re
-import shutil
-import string
-import sys
-import tempfile
-
-from webkitpy.common.memoized import memoized
-from webkitpy.common.system.executive import Executive, ScriptError
-
-from .scm import AuthenticationError, SCM, commit_error_handler
-
-_log = logging.getLogger(__name__)
-
-
-# A mixin class that represents common functionality for SVN and Git-SVN.
-class SVNRepository(object):
- # FIXME: These belong in common.config.urls
- svn_server_host = "svn.webkit.org"
- svn_server_realm = "<http://svn.webkit.org:80> Mac OS Forge"
-
- def has_authorization_for_realm(self, realm, home_directory=os.getenv("HOME")):
- # If we are working on a file:// repository realm will be None
- if realm is None:
- return True
- # ignore false positives for methods implemented in the mixee class. pylint: disable=E1101
- # Assumes find and grep are installed.
- if not os.path.isdir(os.path.join(home_directory, ".subversion")):
- return False
- find_args = ["find", ".subversion", "-type", "f", "-exec", "grep", "-q", realm, "{}", ";", "-print"]
- find_output = self.run(find_args, cwd=home_directory, error_handler=Executive.ignore_error).rstrip()
- if not find_output or not os.path.isfile(os.path.join(home_directory, find_output)):
- return False
- # Subversion either stores the password in the credential file, indicated by the presence of the key "password",
- # or uses the system password store (e.g. Keychain on Mac OS X) as indicated by the presence of the key "passtype".
- # We assume that these keys will not coincide with the actual credential data (e.g. that a person's username
- # isn't "password") so that we can use grep.
- if self.run(["grep", "password", find_output], cwd=home_directory, return_exit_code=True) == 0:
- return True
- return self.run(["grep", "passtype", find_output], cwd=home_directory, return_exit_code=True) == 0
-
-
-class SVN(SCM, SVNRepository):
-
- executable_name = "svn"
-
- _svn_metadata_files = frozenset(['.svn', '_svn'])
-
- def __init__(self, cwd, patch_directories, **kwargs):
- SCM.__init__(self, cwd, **kwargs)
- self._bogus_dir = None
- if patch_directories == []:
- raise Exception(message='Empty list of patch directories passed to SCM.__init__')
- elif patch_directories == None:
- self._patch_directories = [self._filesystem.relpath(cwd, self.checkout_root)]
- else:
- self._patch_directories = patch_directories
-
- @classmethod
- def in_working_directory(cls, path, executive=None):
- if os.path.isdir(os.path.join(path, '.svn')):
- # This is a fast shortcut for svn info that is usually correct for SVN < 1.7,
- # but doesn't work for SVN >= 1.7.
- return True
-
- executive = executive or Executive()
- svn_info_args = [cls.executable_name, 'info']
- exit_code = executive.run_command(svn_info_args, cwd=path, return_exit_code=True)
- return (exit_code == 0)
-
- def find_uuid(self, path):
- if not self.in_working_directory(path):
- return None
- return self.value_from_svn_info(path, 'Repository UUID')
-
- @classmethod
- def value_from_svn_info(cls, path, field_name):
- svn_info_args = [cls.executable_name, 'info']
- # FIXME: This method should use a passed in executive or be made an instance method and use self._executive.
- info_output = Executive().run_command(svn_info_args, cwd=path).rstrip()
- match = re.search("^%s: (?P<value>.+)$" % field_name, info_output, re.MULTILINE)
- if not match:
- raise ScriptError(script_args=svn_info_args, message='svn info did not contain a %s.' % field_name)
- return match.group('value').rstrip('\r')
-
- def find_checkout_root(self, path):
- uuid = self.find_uuid(path)
- # If |path| is not in a working directory, we're supposed to return |path|.
- if not uuid:
- return path
- # Search up the directory hierarchy until we find a different UUID.
- last_path = None
- while True:
- if uuid != self.find_uuid(path):
- return last_path
- last_path = path
- (path, last_component) = self._filesystem.split(path)
- if last_path == path:
- return None
-
- @staticmethod
- def commit_success_regexp():
- return "^Committed revision (?P<svn_revision>\d+)\.$"
-
- def _run_svn(self, args, **kwargs):
- return self.run([self.executable_name] + args, **kwargs)
-
- @memoized
- def svn_version(self):
- return self._run_svn(['--version', '--quiet'])
-
- def has_working_directory_changes(self):
- # FIXME: What about files which are not committed yet?
- return self._run_svn(["diff"], cwd=self.checkout_root, decode_output=False) != ""
-
- def discard_working_directory_changes(self):
- # Make sure there are no locks lying around from a previously aborted svn invocation.
- # This is slightly dangerous, as it's possible the user is running another svn process
- # on this checkout at the same time. However, it's much more likely that we're running
- # under windows and svn just sucks (or the user interrupted svn and it failed to clean up).
- self._run_svn(["cleanup"], cwd=self.checkout_root)
-
- # svn revert -R is not as awesome as git reset --hard.
- # It will leave added files around, causing later svn update
- # calls to fail on the bots. We make this mirror git reset --hard
- # by deleting any added files as well.
- added_files = reversed(sorted(self.added_files()))
- # added_files() returns directories for SVN, we walk the files in reverse path
- # length order so that we remove files before we try to remove the directories.
- self._run_svn(["revert", "-R", "."], cwd=self.checkout_root)
- for path in added_files:
- # This is robust against cwd != self.checkout_root
- absolute_path = self.absolute_path(path)
- # Completely lame that there is no easy way to remove both types with one call.
- if os.path.isdir(path):
- os.rmdir(absolute_path)
- else:
- os.remove(absolute_path)
-
- def status_command(self):
- return [self.executable_name, 'status']
-
- def _status_regexp(self, expected_types):
- field_count = 6 if self.svn_version() > "1.6" else 5
- return "^(?P<status>[%s]).{%s} (?P<filename>.+)$" % (expected_types, field_count)
-
- def _add_parent_directories(self, path):
- """Does 'svn add' to the path and its parents."""
- if self.in_working_directory(path):
- return
- self.add(path)
-
- def add_list(self, paths):
- for path in paths:
- self._add_parent_directories(os.path.dirname(os.path.abspath(path)))
- if self.svn_version() >= "1.7":
- # For subversion client 1.7 and later, need to add '--parents' option to ensure intermediate directories
- # are added; in addition, 1.7 returns an exit code of 1 from svn add if one or more of the requested
- # adds are already under version control, including intermediate directories subject to addition
- # due to --parents
- svn_add_args = ['svn', 'add', '--parents'] + paths
- exit_code = self.run(svn_add_args, return_exit_code=True)
- if exit_code and exit_code != 1:
- raise ScriptError(script_args=svn_add_args, exit_code=exit_code)
- else:
- self._run_svn(["add"] + paths)
-
- def _delete_parent_directories(self, path):
- if not self.in_working_directory(path):
- return
- if set(os.listdir(path)) - self._svn_metadata_files:
- return # Directory has non-trivial files in it.
- self.delete(path)
-
- def delete_list(self, paths):
- for path in paths:
- abs_path = os.path.abspath(path)
- parent, base = os.path.split(abs_path)
- result = self._run_svn(["delete", "--force", base], cwd=parent)
- self._delete_parent_directories(os.path.dirname(abs_path))
- return result
-
- def exists(self, path):
- return not self._run_svn(["info", path], return_exit_code=True, decode_output=False)
-
- def changed_files(self, git_commit=None):
- status_command = [self.executable_name, "status"]
- status_command.extend(self._patch_directories)
- # ACDMR: Addded, Conflicted, Deleted, Modified or Replaced
- return self.run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR"))
-
- def changed_files_for_revision(self, revision):
- # As far as I can tell svn diff --summarize output looks just like svn status output.
- # No file contents printed, thus utf-8 auto-decoding in self.run is fine.
- status_command = [self.executable_name, "diff", "--summarize", "-c", revision]
- return self.run_status_and_extract_filenames(status_command, self._status_regexp("ACDMR"))
-
- def revisions_changing_file(self, path, limit=5):
- revisions = []
- # svn log will exit(1) (and thus self.run will raise) if the path does not exist.
- log_command = ['log', '--quiet', '--limit=%s' % limit, path]
- for line in self._run_svn(log_command, cwd=self.checkout_root).splitlines():
- match = re.search('^r(?P<revision>\d+) ', line)
- if not match:
- continue
- revisions.append(int(match.group('revision')))
- return revisions
-
- def conflicted_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("C"))
-
- def added_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("A"))
-
- def deleted_files(self):
- return self.run_status_and_extract_filenames(self.status_command(), self._status_regexp("D"))
-
- @staticmethod
- def supports_local_commits():
- return False
-
- def display_name(self):
- return "svn"
-
- def svn_revision(self, path):
- return self.value_from_svn_info(path, 'Revision')
-
- def timestamp_of_revision(self, path, revision):
- # We use --xml to get timestamps like 2013-02-08T08:18:04.964409Z
- repository_root = self.value_from_svn_info(self.checkout_root, 'Repository Root')
- info_output = Executive().run_command([self.executable_name, 'log', '-r', revision, '--xml', repository_root], cwd=path).rstrip()
- match = re.search(r"^<date>(?P<value>.+)</date>\r?$", info_output, re.MULTILINE)
- return match.group('value')
-
- # FIXME: This method should be on Checkout.
- def create_patch(self, git_commit=None, changed_files=None):
- """Returns a byte array (str()) representing the patch file.
- Patch files are effectively binary since they may contain
- files of multiple different encodings."""
- if changed_files == []:
- return ""
- elif changed_files == None:
- changed_files = []
- return self.run([self.script_path("svn-create-patch")] + changed_files,
- cwd=self.checkout_root, return_stderr=False,
- decode_output=False)
-
- def committer_email_for_revision(self, revision):
- return self._run_svn(["propget", "svn:author", "--revprop", "-r", revision]).rstrip()
-
- def contents_at_revision(self, path, revision):
- """Returns a byte array (str()) containing the contents
- of path @ revision in the repository."""
- remote_path = "%s/%s" % (self._repository_url(), path)
- return self._run_svn(["cat", "-r", revision, remote_path], decode_output=False)
-
- def diff_for_revision(self, revision):
- # FIXME: This should probably use cwd=self.checkout_root
- return self._run_svn(['diff', '-c', revision])
-
- def _bogus_dir_name(self):
- rnd = ''.join(random.sample(string.ascii_letters, 5))
- if sys.platform.startswith("win"):
- parent_dir = tempfile.gettempdir()
- else:
- parent_dir = sys.path[0] # tempdir is not secure.
- return os.path.join(parent_dir, "temp_svn_config_" + rnd)
-
- def _setup_bogus_dir(self, log):
- self._bogus_dir = self._bogus_dir_name()
- if not os.path.exists(self._bogus_dir):
- os.mkdir(self._bogus_dir)
- self._delete_bogus_dir = True
- else:
- self._delete_bogus_dir = False
- if log:
- log.debug(' Html: temp config dir: "%s".', self._bogus_dir)
-
- def _teardown_bogus_dir(self, log):
- if self._delete_bogus_dir:
- shutil.rmtree(self._bogus_dir, True)
- if log:
- log.debug(' Html: removed temp config dir: "%s".', self._bogus_dir)
- self._bogus_dir = None
-
- def diff_for_file(self, path, log=None):
- self._setup_bogus_dir(log)
- try:
- args = ['diff']
- if self._bogus_dir:
- args += ['--config-dir', self._bogus_dir]
- args.append(path)
- return self._run_svn(args, cwd=self.checkout_root)
- finally:
- self._teardown_bogus_dir(log)
-
- def show_head(self, path):
- return self._run_svn(['cat', '-r', 'BASE', path], decode_output=False)
-
- def _repository_url(self):
- return self.value_from_svn_info(self.checkout_root, 'URL')
-
- def apply_reverse_diff(self, revision):
- # '-c -revision' applies the inverse diff of 'revision'
- svn_merge_args = ['merge', '--non-interactive', '-c', '-%s' % revision, self._repository_url()]
- _log.warning("svn merge has been known to take more than 10 minutes to complete. It is recommended you use git for rollouts.")
- _log.debug("Running 'svn %s'" % " ".join(svn_merge_args))
- # FIXME: Should this use cwd=self.checkout_root?
- self._run_svn(svn_merge_args)
-
- def revert_files(self, file_paths):
- # FIXME: This should probably use cwd=self.checkout_root.
- self._run_svn(['revert'] + file_paths)
-
- def commit_with_message(self, message, username=None, password=None, git_commit=None, force_squash=False, changed_files=None):
- # git-commit and force are not used by SVN.
- svn_commit_args = ["commit"]
-
- if not username and not self.has_authorization_for_realm(self.svn_server_realm):
- raise AuthenticationError(self.svn_server_host)
- if username:
- svn_commit_args.extend(["--username", username])
-
- svn_commit_args.extend(["-m", message])
-
- if changed_files:
- svn_commit_args.extend(changed_files)
-
- return self._run_svn(svn_commit_args, cwd=self.checkout_root, error_handler=commit_error_handler)
-
- def svn_commit_log(self, svn_revision):
- svn_revision = self.strip_r_from_svn_revision(svn_revision)
- return self._run_svn(['log', '--non-interactive', '--revision', svn_revision])
-
- def last_svn_commit_log(self):
- # BASE is the checkout revision, HEAD is the remote repository revision
- # http://svnbook.red-bean.com/en/1.0/ch03s03.html
- return self.svn_commit_log('BASE')
-
- def svn_blame(self, path):
- return self._run_svn(['blame', path])
-
- def propset(self, pname, pvalue, path):
- dir, base = os.path.split(path)
- return self._run_svn(['pset', pname, pvalue, base], cwd=dir)
-
- def propget(self, pname, path):
- dir, base = os.path.split(path)
- return self._run_svn(['pget', pname, base], cwd=dir).encode('utf-8').rstrip("\n")