Index: third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py |
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py |
index dc790442463defccbd0b90f9f4ab79a8fd9d93aa..ab21b9da8fe44165f74b7c703f487db75931087f 100644 |
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py |
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/scm/git.py |
@@ -28,23 +28,19 @@ |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
import datetime |
+import logging |
import re |
-from webkitpy.common.checkout.scm.scm import SCM |
from webkitpy.common.memoized import memoized |
from webkitpy.common.system.executive import Executive, ScriptError |
+from webkitpy.common.system.filesystem import FileSystem |
+_log = logging.getLogger(__name__) |
-class AmbiguousCommitError(Exception): |
- def __init__(self, num_local_commits, has_working_directory_changes): |
- Exception.__init__(self, "Found %s local commits and the working directory is %s" % ( |
- num_local_commits, ["clean", "not clean"][has_working_directory_changes])) |
- self.num_local_commits = num_local_commits |
- self.has_working_directory_changes = has_working_directory_changes |
- |
- |
-class Git(SCM): |
+class Git(object): |
+ # Unless otherwise specified, methods are expected to return paths relative |
+ # to self.checkout_root. |
# Git doesn't appear to document error codes, but seems to return |
# 1 or 128, mostly. |
@@ -52,15 +48,33 @@ class Git(SCM): |
executable_name = 'git' |
- def __init__(self, cwd, **kwargs): |
- SCM.__init__(self, cwd, **kwargs) |
- |
- def _run_git(self, command_args, **kwargs): |
+ def __init__(self, cwd, executive=None, filesystem=None): |
+ self.cwd = cwd |
+ self._executive = executive or Executive() |
+ self._filesystem = filesystem or FileSystem() |
+ self.checkout_root = self.find_checkout_root(self.cwd) |
+ |
+ def _run_git(self, |
+ command_args, |
+ cwd=None, |
+ input=None, # pylint: disable=redefined-builtin |
+ timeout_seconds=None, |
+ decode_output=True, |
+ return_exit_code=False): |
full_command_args = [self.executable_name] + command_args |
- full_kwargs = kwargs |
- if 'cwd' not in full_kwargs: |
- full_kwargs['cwd'] = self.checkout_root |
- return self._run(full_command_args, **full_kwargs) |
+ cwd = cwd or self.checkout_root |
+ return self._executive.run_command( |
+ full_command_args, |
+ cwd=cwd, |
+ input=input, |
+ timeout_seconds=timeout_seconds, |
+ return_exit_code=return_exit_code, |
+ decode_output=decode_output) |
+ |
+ # SCM always returns repository relative path, but sometimes we need |
+ # absolute paths to pass to rm, etc. |
+ def absolute_path(self, repository_relative_path): |
+ return self._filesystem.join(self.checkout_root, repository_relative_path) |
@classmethod |
def in_working_directory(cls, path, executive=None): |
@@ -130,21 +144,13 @@ class Git(SCM): |
unstaged_changes[path] = line[1] |
return unstaged_changes |
- def status_command(self): |
- # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead. |
- # No file contents printed, thus utf-8 autodecoding in self.run is fine. |
- return [self.executable_name, "diff", "--name-status", "--no-renames", "HEAD"] |
- |
- def _status_regexp(self, expected_types): |
- return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types |
- |
def add_all(self, pathspec=None): |
command = ['add', '--all'] |
if pathspec: |
command.append(pathspec) |
return self._run_git(command) |
- def add_list(self, paths, return_exit_code=False, recurse=True): |
+ def add_list(self, paths, return_exit_code=False): |
return self._run_git(["add"] + paths, return_exit_code=return_exit_code) |
def delete_list(self, paths): |
@@ -208,11 +214,28 @@ class Git(SCM): |
# Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R) |
return self._run_status_and_extract_filenames(status_command, self._status_regexp("ADM")) |
- def _added_files(self): |
+ 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")) |
+ def _run_status_and_extract_filenames(self, status_command, status_regexp): |
+ filenames = [] |
+ # We run with cwd=self.checkout_root so that returned-paths are root-relative. |
+ for line in self._run_git(status_command, cwd=self.checkout_root).splitlines(): |
+ match = re.search(status_regexp, line) |
+ if not match: |
+ continue |
+ # status = match.group('status') |
+ filename = match.group('filename') |
+ filenames.append(filename) |
+ return filenames |
+ |
+ def status_command(self): |
+ # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead. |
+ # No file contents printed, thus utf-8 autodecoding in self.run is fine. |
+ return ["diff", "--name-status", "--no-renames", "HEAD"] |
+ |
+ def _status_regexp(self, expected_types): |
+ return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types |
@staticmethod |
def supports_local_commits(): |
@@ -233,6 +256,7 @@ class Git(SCM): |
return int(match.group('commit_position')) |
def commit_position(self, path): |
+ """Returns the latest chromium commit position found in the checkout.""" |
git_log = self.most_recent_log_matching('Cr-Commit-Position:', path) |
return self._commit_position_from_git_log(git_log) |
@@ -260,21 +284,33 @@ class Git(SCM): |
Patch files are effectively binary since they may contain |
files of multiple different encodings. |
""" |
+ order = self._patch_order() |
+ command = [ |
+ 'diff', |
+ '--binary', |
+ '--no-color', |
+ "--no-ext-diff", |
+ "--full-index", |
+ "--no-renames", |
+ "--src-prefix=a/", |
+ "--dst-prefix=b/", |
+ |
+ ] |
+ if order: |
+ command.append(order) |
+ command += [self._merge_base(git_commit), "--"] |
+ if changed_files: |
+ command += changed_files |
+ return self._run_git(command, decode_output=False, cwd=self.checkout_root) |
+ def _patch_order(self): |
# Put code changes at the top of the patch and layout tests |
# at the bottom, this makes for easier reviewing. |
config_path = self._filesystem.dirname(self._filesystem.path_to_module('webkitpy.common.config')) |
order_file = self._filesystem.join(config_path, 'orderfile') |
- order = "" |
if self._filesystem.exists(order_file): |
- order = "-O%s" % order_file |
- |
- command = [self.executable_name, 'diff', '--binary', '--no-color', "--no-ext-diff", |
- "--full-index", "--no-renames", "--src-prefix=a/", "--dst-prefix=b/", |
- order, self._merge_base(git_commit), "--"] |
- if changed_files: |
- command += changed_files |
- return self._run(command, decode_output=False, cwd=self.checkout_root) |
+ return "-O%s" % order_file |
+ return "" |
@memoized |
def commit_position_from_git_commit(self, git_commit): |
@@ -312,9 +348,6 @@ class Git(SCM): |
command = ['commit', '--all', '-F', '-'] |
self._run_git(command, input=message) |
- # These methods are git specific and are meant to provide support for the Git oriented workflow |
- # that Blink is moving towards, hence there are no equivalent methods in the SVN class. |
- |
def pull(self, timeout_seconds=None): |
self._run_git(['pull'], timeout_seconds=timeout_seconds) |
@@ -324,7 +357,7 @@ class Git(SCM): |
def git_commits_since(self, commit): |
return self._run_git(['log', commit + '..master', '--format=%H', '--reverse']).split() |
- def git_commit_detail(self, commit, format=None): |
+ def git_commit_detail(self, commit, format=None): # pylint: disable=redefined-builtin |
args = ['log', '-1', commit] |
if format: |
args.append('--format=' + format) |