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 logging | |
| 6 import os | |
| 7 from urlparse import urlparse | |
| 8 import subprocess | |
| 9 import threading | |
| 10 | |
| 11 from lib.gitiles import local_git_parsers | |
| 12 from lib.gitiles import repo_util | |
| 13 from lib.gitiles.git_repository import GitRepository | |
| 14 | |
| 15 _CHANGELOG_FORMAT_STRING = ('commit %H%n' | |
| 16 'author %an%n' | |
| 17 'author-mail %ae%n' | |
| 18 'author-time %ad%n%n' | |
| 19 'committer %cn%n' | |
| 20 'committer-mail %ce%n' | |
| 21 'committer-time %cd%n%n' | |
| 22 '--Message start--%n%B%n--Message end--%n') | |
| 23 _CHANGELOGS_FORMAT_STRING = ('**Changelog start**%%n%s' % | |
| 24 _CHANGELOG_FORMAT_STRING) | |
| 25 CHECKOUT_ROOT_DIR = os.path.join(os.path.expanduser('~'), '.local_checkouts') | |
| 26 | |
| 27 | |
| 28 class LocalGitRepository(GitRepository): | |
| 29 """Represents local checkout of git repository on chromium host. | |
| 30 | |
| 31 Note, to checkout internal repos automatically which you have access to, | |
| 32 follow the instructions in ('https://g3doc.corp.google.com/company/teams/ | |
| 33 chrome/chrome_build_instructions.md? | |
| 34 cl=head#authentication-to-git-servers-chrome-internalgooglesourcecom') first. | |
| 35 """ | |
| 36 lock = threading.Lock() | |
| 37 # Keep track all the updated repos, so every repo only get updated once. | |
| 38 _updated_repos = set() | |
| 39 def __init__(self, repo_url=None): | |
| 40 self._host = None | |
| 41 self._repo_path = None | |
| 42 self._repo_url = None | |
| 43 self.repo_url = repo_url | |
|
wrengr
2016/11/01 20:26:52
I still think you should rename the repo_url and _
Sharu Jiang
2016/11/05 01:18:15
Done.
| |
| 44 | |
| 45 @property | |
| 46 def repo_path(self): | |
| 47 return self._repo_path | |
| 48 | |
| 49 @property | |
| 50 def real_repo_path(self): | |
| 51 """Absolute path of the local repository.""" | |
| 52 return os.path.join(CHECKOUT_ROOT_DIR, self._host, self.repo_path) | |
| 53 | |
| 54 @property | |
| 55 def repo_url(self): | |
| 56 """Url of remote repository which the local repo checks out from.""" | |
| 57 return self._repo_url | |
| 58 | |
| 59 @repo_url.setter | |
| 60 def repo_url(self, repo_url): | |
| 61 if self._repo_url == repo_url: | |
| 62 return | |
| 63 | |
| 64 self._repo_url = repo_url | |
| 65 if not self._repo_url: | |
| 66 return | |
| 67 | |
| 68 parsed_url = urlparse(repo_url) | |
| 69 self._host = parsed_url.netloc | |
| 70 # Remove the / in the front of path. | |
| 71 self._repo_path = parsed_url.path[1:] | |
| 72 | |
| 73 self._CloneOrUpdateRepoIfNeeded() | |
| 74 | |
| 75 def _CloneOrUpdateRepoIfNeeded(self): | |
| 76 """Clones repo, or update it if it didn't got updated before.""" | |
| 77 if self.repo_url in LocalGitRepository._updated_repos: | |
| 78 return | |
| 79 | |
| 80 with LocalGitRepository.lock: | |
| 81 # Clone the repo if needed. | |
| 82 if not os.path.exists(self.real_repo_path): | |
|
wrengr
2016/11/01 20:26:53
In general it's bad to check whether a file/direct
Sharu Jiang
2016/11/05 01:18:15
what if one process is git cloning the repo, and a
wrengr
2016/11/08 19:32:37
Probably. Depends on whether git is smart enough t
| |
| 83 try: | |
| 84 subprocess.check_call(['git', 'clone', | |
| 85 self.repo_url, self.real_repo_path]) | |
| 86 except subprocess.CalledProcessError as e: # pragma: no cover. | |
| 87 logging.error('Exception while cloning %s: %s', self.repo_url, e) | |
| 88 return | |
| 89 # Update repo if it's already cloned. | |
| 90 else: | |
| 91 try: | |
| 92 # Disable verbose of cd and git pull. | |
| 93 with open(os.devnull, 'w') as null_handle: | |
| 94 subprocess.check_call( | |
| 95 'cd %s && git pull' % self.real_repo_path, | |
| 96 stdout=null_handle, stderr=null_handle, shell=True) | |
| 97 except subprocess.CalledProcessError as e: # pragma: no cover. | |
| 98 logging.error('Exception while updating %s: %s', self.repo_path, e) | |
| 99 return | |
| 100 | |
| 101 LocalGitRepository._updated_repos.add(self.repo_url) | |
| 102 | |
| 103 def _GetFinalCommand(self, command, format_date=False): | |
| 104 # Change local time to utc time. | |
| 105 if format_date: | |
| 106 command = 'TZ=UTC %s --date=format-local:"%s"' % ( | |
| 107 command, local_git_parsers.DATETIME_FORMAT) | |
| 108 return 'cd %s && %s' % (self.real_repo_path, command) | |
| 109 | |
| 110 def GetChangeLog(self, revision): | |
| 111 """Returns the change log of the given revision.""" | |
| 112 revision = 'HEAD' if revision == 'master' else revision | |
| 113 command = ('git log --pretty=format:"%s" --max-count=1 --raw ' | |
| 114 '--no-abbrev %s' % (_CHANGELOG_FORMAT_STRING, revision)) | |
| 115 output = repo_util.GetCommandOutput(self._GetFinalCommand(command, True)) | |
| 116 if not output: | |
| 117 return None | |
| 118 | |
| 119 return local_git_parsers.GitChangeLogParser()(output, self.repo_url) | |
| 120 | |
| 121 def GetChangeLogs(self, start_revision, end_revision): # pylint: disable=W | |
| 122 """Returns change log list in (start_revision, end_revision].""" | |
| 123 start_revision = 'HEAD' if start_revision == 'master' else start_revision | |
|
wrengr
2016/11/01 20:26:53
Since this conditional rewrite is used in a bunch
Sharu Jiang
2016/11/05 01:18:15
Done.
| |
| 124 end_revision = 'HEAD' if end_revision == 'master' else end_revision | |
| 125 command = ('git log --pretty=format:"%s" --raw ' | |
| 126 '--no-abbrev %s' % (_CHANGELOGS_FORMAT_STRING, | |
| 127 '%s..%s' % (start_revision, end_revision))) | |
| 128 output = repo_util.GetCommandOutput(self._GetFinalCommand(command, True)) | |
| 129 if not output: | |
| 130 return None | |
| 131 | |
| 132 return local_git_parsers.GitChangeLogsParser()(output, self.repo_url) | |
|
wrengr
2016/11/01 20:26:52
Presumably you want to save the result of local_gi
Sharu Jiang
2016/11/05 01:18:15
Acknowledged.
| |
| 133 | |
| 134 def GetChangeDiff(self, revision, path=None): # pylint: disable=W | |
| 135 """Returns the diff of the given revision.""" | |
| 136 revision = 'HEAD' if revision == 'master' else revision | |
| 137 command = 'git log --format="" --max-count=1 %s' % revision | |
| 138 if path: | |
| 139 command += ' -p %s' % path | |
| 140 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 141 if not output: | |
| 142 return None | |
| 143 | |
| 144 return local_git_parsers.GitDiffParser()(output) | |
| 145 | |
| 146 def GetBlame(self, path, revision): | |
| 147 """Returns blame of the file at ``path`` of the given revision.""" | |
| 148 revision = 'HEAD' if revision == 'master' else revision | |
| 149 command = 'git blame --incremental %s %s' % (path, revision) | |
| 150 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 151 if not output: | |
| 152 return None | |
| 153 | |
| 154 return local_git_parsers.GitBlameParser()(output, path, revision) | |
| 155 | |
| 156 def GetSource(self, path, revision): | |
| 157 """Returns source code of the file at ``path`` of the given revision.""" | |
| 158 # Check whether the requested file exist or not. | |
| 159 if not os.path.isfile(os.path.join(self.real_repo_path, path)): | |
| 160 return None | |
| 161 revision = 'HEAD' if revision == 'master' else revision | |
| 162 command = 'git show %s:%s' % (revision, path) | |
| 163 output = repo_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 164 if not output: | |
| 165 return None | |
| 166 | |
| 167 return local_git_parsers.GitSourceParser()(output) | |
| OLD | NEW |