Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import os | |
| 6 import subprocess | |
| 7 import threading | |
| 8 | |
| 9 from common import local_git_parsers | |
| 10 from common import repo_util | |
| 11 from common.repository import Repository | |
| 12 | |
| 13 _CHANGELOG_FORMAT_STRING = ('commit %H%n' | |
| 14 'author %an%n' | |
| 15 'author-mail %ae%n' | |
| 16 'author-time %ad%n%n' | |
| 17 'committer %cn%n' | |
| 18 'committer-mail %ce%n' | |
| 19 'committer-time %cd%n%n' | |
| 20 '--Message start--%n%B%n--Message end--%n') | |
| 21 _CHANGELOGS_FORMAT_STRING = ('**Changelog start**%%n%s' % | |
| 22 _CHANGELOG_FORMAT_STRING) | |
| 23 CHECKOUT_ROOT_DIR = os.path.join(os.path.expanduser('~'), '.local_checkouts') | |
| 24 | |
| 25 | |
| 26 class LocalGitRepository(Repository): | |
| 27 """Represents local checkout of git repository on chromium host. | |
| 28 | |
| 29 Note, to checkout internal repos automatically which you have access to, | |
| 30 follow the instructions in ('https://g3doc.corp.google.com/company/teams/ | |
| 31 chrome/chrome_build_instructions.md? | |
| 32 cl=head#authentication-to-git-servers-chrome-internalgooglesourcecom') first. | |
| 33 """ | |
| 34 lock = threading.Lock() | |
| 35 # Keep track all the updated repos, so every repo only get updated once. | |
| 36 updated_repos = set() | |
|
wrengr
2016/10/25 18:12:18
I'd think this should be private/protected. Would
Sharu Jiang
2016/10/26 01:31:15
no, it is neither written or read by other code. D
| |
| 37 def __init__(self, repo_url=None): | |
| 38 self._host = None | |
| 39 self._repo_path = None | |
| 40 self._repo_url = None | |
| 41 self.repo_url = repo_url | |
|
wrengr
2016/10/25 18:12:18
What's the difference between self._repo_url vs se
Sharu Jiang
2016/10/26 01:31:15
the self._repo_url set the initial default value,
| |
| 42 | |
| 43 @property | |
| 44 def repo_path(self): | |
| 45 return self._repo_path | |
| 46 | |
| 47 @property | |
| 48 def real_repo_path(self): | |
|
wrengr
2016/10/25 18:12:18
Needs docstring explaining what "real" means here.
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 49 return os.path.join(CHECKOUT_ROOT_DIR, self._host, self.repo_path) | |
| 50 | |
| 51 @property | |
| 52 def repo_url(self): | |
|
wrengr
2016/10/25 18:12:18
Needs docstring explaining how this differs from t
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 53 return self._repo_url | |
| 54 | |
| 55 @repo_url.setter | |
| 56 def repo_url(self, repo_url): | |
| 57 if not repo_url or self._repo_url == repo_url: | |
| 58 return | |
| 59 | |
| 60 self._repo_url = repo_url | |
| 61 url_parts = repo_url.split('https://', 1)[1].split('/') | |
|
wrengr
2016/10/25 18:12:18
This regex seems very fragile. Don't we have a lib
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 62 | |
| 63 self._host = url_parts[0] | |
| 64 self._repo_path = '/'.join(url_parts[1:]) | |
|
wrengr
2016/10/25 18:12:18
Calling join after split is expensive. If sticking
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 65 self._CloneOrUpdateRepoIfNeeded() | |
| 66 | |
| 67 def _CloneOrUpdateRepoIfNeeded(self): | |
| 68 """Clones repo, or update it if it didn't got updated before.""" | |
| 69 if self.repo_url in LocalGitRepository.updated_repos: | |
| 70 return | |
| 71 | |
| 72 real_repo_path = os.path.join(CHECKOUT_ROOT_DIR, self.repo_path) | |
| 73 with LocalGitRepository.lock: | |
| 74 # Clone the repo if needed. | |
| 75 if not os.path.exists(real_repo_path): | |
| 76 subprocess.call(['git', 'clone', self.repo_url, real_repo_path]) | |
|
wrengr
2016/10/25 18:12:18
We should check the return value to make sure thin
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 77 # Update repo if it's already cloned. | |
| 78 else: | |
| 79 # Disable verbose of git pull. | |
| 80 with open(os.devnull, 'w') as null_handle: | |
| 81 subprocess.check_call( | |
|
wrengr
2016/10/25 18:12:18
Ditto. Especially given as we're silencing stderr.
Sharu Jiang
2016/10/26 01:31:15
Done.
| |
| 82 'cd %s; git pull' % real_repo_path, | |
| 83 stdout=null_handle, | |
| 84 stderr=null_handle, | |
| 85 shell=True) | |
| 86 | |
| 87 LocalGitRepository.updated_repos.add(self.repo_url) | |
| 88 | |
| 89 def _GetFinalCommand(self, command): | |
| 90 # Change local time to utc time. | |
| 91 command = 'TZ=UTC %s --date=format-local:"%s"' % ( | |
|
stgao
2016/10/25 23:04:23
Should TZ=UTC be set through env? Does this work a
Sharu Jiang
2016/10/26 01:31:15
Do we want to use UTC everywhere? I think it may b
| |
| 92 command, local_git_parsers.DATETIME_FORMAT) | |
| 93 return 'cd %s; %s' % (self.real_repo_path, command) | |
|
wrengr
2016/10/25 18:12:18
Should be 'cd %s && %s'. If the cd fails for some
Sharu Jiang
2016/10/26 01:31:15
Done. Good to know this usage :)
| |
| 94 | |
| 95 def GetChangeLog(self, revision): | |
| 96 """Returns the change log of the given revision.""" | |
| 97 command = ('git log --pretty=format:"%s" --max-count=1 --raw ' | |
| 98 '--no-abbrev %s' % (_CHANGELOG_FORMAT_STRING, revision)) | |
| 99 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 100 change_log = local_git_parsers.GitChangeLogParser()(output, self.repo_url) | |
| 101 return change_log | |
| 102 | |
| 103 def GetChangeLogs(self, start_revision, end_revision): # pylint: disable=W | |
| 104 """Returns change log list in (start_revision, end_revision].""" | |
| 105 command = ('git log --pretty=format:"%s" --raw ' | |
| 106 '--no-abbrev %s' % (_CHANGELOGS_FORMAT_STRING, | |
| 107 '%s..%s' % (start_revision, end_revision))) | |
| 108 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 109 return local_git_parsers.GitChangeLogsParser()(output, self.repo_url) | |
| 110 | |
| 111 def GetChangeDiff(self, revision, path=None): # pylint: disable=W | |
| 112 """Returns the diff of the given revision.""" | |
| 113 command = 'git log --format="" --max-count=1 %s' % revision | |
| 114 if path: | |
| 115 command += ' -p %s' % path | |
| 116 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 117 return local_git_parsers.GitDiffParser()(output) | |
| 118 | |
| 119 def GetBlame(self, path, revision): | |
| 120 """Returns blame of the file at ``path`` of the given revision.""" | |
|
wrengr
2016/10/25 18:12:18
Is our style to use double backticks when referrin
stgao
2016/10/25 23:04:23
In infra, double backticks is the way to go.
Sharu Jiang
2016/10/26 01:31:15
Acknowledged.
| |
| 121 command = 'git blame --porcelain %s %s' % (path, revision) | |
| 122 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 123 return local_git_parsers.GitBlameParser()(output, path, revision) | |
| 124 | |
| 125 def GetSource(self, path, revision): | |
| 126 """Returns source code of the file at ``path`` of the given revision.""" | |
| 127 # Check whether the requested file exist or not. | |
| 128 if not os.path.isfile(os.path.join(self.real_repo_path, path)): | |
| 129 return None | |
| 130 | |
| 131 command = 'git show %s:%s' % (revision, path) | |
| 132 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 133 return local_git_parsers.GitSourceParser()(output) | |
| OLD | NEW |