Chromium Code Reviews| Index: appengine/findit/common/local_git_repository.py |
| diff --git a/appengine/findit/common/local_git_repository.py b/appengine/findit/common/local_git_repository.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..81eedac750082701ba320decc6087fdddf5c6ad8 |
| --- /dev/null |
| +++ b/appengine/findit/common/local_git_repository.py |
| @@ -0,0 +1,145 @@ |
| +# Copyright 2014 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. |
| + |
| +import logging |
| +import os |
| +import subprocess |
| +import threading |
| + |
| +from common import local_git_parsers |
| +from common import repo_util |
| +from common.repository import Repository |
| + |
| +_CHANGELOG_FORMAT_STRING = ('commit %H%n' |
| + 'author %an%n' |
| + 'author-mail %ae%n' |
| + 'author-time %at%n%n' |
| + 'committer %cn%n' |
| + 'committer-mail %ce%n' |
| + 'committer-time %ct%n%n' |
| + '--Message start--%n%B%n--Message end--%n') |
| +_CHANGELOGS_FORMAT_STRING = ('**Changelog start**%%n%s' % |
| + _CHANGELOG_FORMAT_STRING) |
| +CHECKOUT_ROOT_DIR = os.path.join(os.path.expanduser('~'), '.local_checkouts') |
| + |
| + |
| +class LocalGitRepository(Repository): |
| + """Represents local checkout of git repository on chromium host. |
| + |
| + Note, to checkout internal repos automatically which you have access to, |
| + follow the instructions in ('https://g3doc.corp.google.com/company/teams/ |
| + chrome/chrome_build_instructions.md? |
| + cl=head#authentication-to-git-servers-chrome-internalgooglesourcecom') first. |
| + """ |
| + lock = threading.Lock() |
| + # Keep track all the updated repos, so every repo only get updated once. |
| + updated_repos = set() |
| + def __init__(self, repo_url=None): |
| + self._repo_path = None |
| + self._repo_url = None |
| + self.repo_url = repo_url |
| + |
| + @property |
| + def repo_path(self): |
| + return self._repo_path |
| + |
| + @property |
| + def repo_url(self): |
| + return self._repo_url |
| + |
| + @repo_url.setter |
| + def repo_url(self, repo_url): |
| + if not repo_url or (hasattr(self, '_repo_url') and |
|
stgao
2016/10/22 00:43:35
Why hasattr is needed? Isn't self._repo_url alread
Sharu Jiang
2016/10/25 06:45:16
Oops, should have deleted.
|
| + self._repo_url == repo_url): |
| + return |
| + |
| + self._repo_url = repo_url |
| + http_trimmed_url = repo_url.split('https://', 1)[1] |
| + host_trimmed_url = '/'.join(http_trimmed_url.split('/')[1:]) |
| + |
| + self._repo_path = host_trimmed_url |
| + self._CloneOrUpdateRepoIfNeeded() |
| + |
| + def _CloneOrUpdateRepoIfNeeded(self): |
| + """Clones repo, or update it if it didn't got updated before.""" |
| + if self.repo_url in LocalGitRepository.updated_repos: |
| + return |
| + |
| + real_repo_path = os.path.join(CHECKOUT_ROOT_DIR, self.repo_path) |
| + with LocalGitRepository.lock: |
| + # Clone the repo if needed. |
| + if not os.path.exists(real_repo_path): |
| + subprocess.call(['git', 'clone', self.repo_url, real_repo_path]) |
| + # Update repo if it's already cloned. |
| + else: |
| + # Disable verbose of git pull. |
| + with open(os.devnull, 'w') as null_handle: |
| + subprocess.check_call( |
| + 'cd %s; git pull' % real_repo_path, |
| + stdout=null_handle, |
| + stderr=null_handle, |
| + shell=True) |
| + |
| + LocalGitRepository.updated_repos.add(self.repo_url) |
| + |
| + def _GetLocalGitCommandOutput(self, command): |
| + """Gets the output stream of a git command for a local git repo. |
| + |
| + If the corresponding git repo_path is not downloaded yet, download it from |
| + https://chromium.googlesource.com. |
| + |
| + Args: |
| + command (str): Command to execute at this repo to get output. |
| + |
| + Return: |
| + Output steam of the git command. |
| + """ |
| + real_repo_path = os.path.join(CHECKOUT_ROOT_DIR, self.repo_path) |
| + command = 'cd %s; %s' % (real_repo_path, command) |
| + p = subprocess.Popen( |
| + command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) |
| + return p.communicate()[0] |
| + |
| + def GetChangeLog(self, revision): |
| + """Returns the change log of the given revision.""" |
| + command = ('git log --pretty=format:"%s" --max-count=1 --raw ' |
| + '--no-abbrev %s' % (_CHANGELOG_FORMAT_STRING, revision)) |
| + output = self._GetLocalGitCommandOutput(command) |
| + change_log = local_git_parsers.GitChangeLogParser()(output) |
| + change_log.commit_url = '%s/+/%s' % (self.repo_url, change_log.revision) |
| + return change_log |
| + |
| + def GetChangeLogs(self, start_revision, end_revision): # pylint: disable=W |
| + """Returns change log list in (start_revision, end_revision].""" |
| + command = ('git log --pretty=format:"%s" --raw ' |
| + '--no-abbrev %s' % (_CHANGELOGS_FORMAT_STRING, |
| + '%s..%s' % (start_revision, end_revision))) |
| + output = self._GetLocalGitCommandOutput(command) |
| + return local_git_parsers.GitChangeLogsParser()(output) |
| + |
| + def GetChangeDiff(self, revision, path=None): # pylint: disable=W |
| + """Returns the diff of the given revision.""" |
| + command = 'git log --format="" --max-count=1 %s' % revision |
| + if path: |
| + command += ' -p %s' % path |
| + output = self._GetLocalGitCommandOutput(command) |
| + return local_git_parsers.GitDiffParser()(output) |
| + |
| + def GetBlame(self, path, revision): |
| + """Returns blame of the file at ``path`` of the given revision.""" |
| + command = 'git blame --porcelain %s %s' % (path, revision) |
| + output = self._GetLocalGitCommandOutput(command, path) |
| + return local_git_parsers.GitBlameParser()(output) |
| + |
| + def GetSource(self, path, revision): |
| + """Returns source code of the file at ``path`` of the given revision.""" |
| + # Check whether the requested file exist or not. |
| + if not os.path.isfile(os.path.join( |
| + os.path.join(CHECKOUT_ROOT_DIR, self.repo_path), |
| + path)): |
| + return None |
| + |
| + command = 'git show %s:%s' % (revision, path) |
| + output = self._GetLocalGitCommandOutput(command, path) |
| + return local_git_parsers.GitSourceParser()(output) |