Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
|
chanli
2016/11/08 01:56:20
Nit: 2014 -> 2016. Same for other added files
Sharu Jiang
2016/11/11 00:29:06
Done.
| |
| 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 git_checkout import local_git_parsers | |
| 12 from lib.gitiles.git_repository import GitRepository | |
| 13 import script_util | |
| 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 def ConvertRemoteCommitToLocal(revision): | |
| 29 """Converts remote commit from gitile to local git checkout revision.""" | |
| 30 return 'HEAD' if revision == 'master' else revision | |
| 31 | |
| 32 | |
| 33 class LocalGitRepository(GitRepository): | |
| 34 """Represents local checkout of git repository on chromium host. | |
| 35 | |
| 36 Note, to checkout internal repos automatically which you have access to, | |
| 37 follow the instructions in ('https://g3doc.corp.google.com/company/teams/ | |
|
stgao
2016/11/08 21:56:36
Could we use a go link instead?
wrengr
2016/11/10 21:27:04
I like the idea, but iirc policy is that go links
Sharu Jiang
2016/11/11 00:29:06
we needs to submit a proposal and get approved to
stgao
2016/11/11 01:07:59
go link seems fine as it is used a lot in the infr
Sharu Jiang
2016/11/11 23:43:49
Done.
| |
| 38 chrome/chrome_build_instructions.md? | |
| 39 cl=head#authentication-to-git-servers-chrome-internalgooglesourcecom') first. | |
| 40 """ | |
| 41 lock = threading.Lock() | |
| 42 # Keep track all the updated repos, so every repo only get updated once. | |
| 43 _updated_repos = set() | |
| 44 def __init__(self, repo_url=None): | |
|
stgao
2016/11/08 21:56:36
empty line above.
Sharu Jiang
2016/11/11 00:29:05
Done.
| |
| 45 self._host = None | |
| 46 self._repo_path = None | |
| 47 self._repo_url = None | |
| 48 self._SetFieldsFromRepoUrl(repo_url) | |
| 49 self.changelog_parser = local_git_parsers.GitChangeLogParser() | |
| 50 self.changelogs_parser = local_git_parsers.GitChangeLogsParser() | |
| 51 self.blame_parser = local_git_parsers.GitBlameParser() | |
| 52 self.diff_parser = local_git_parsers.GitDiffParser() | |
| 53 self.source_parser = local_git_parsers.GitSourceParser() | |
| 54 | |
| 55 @property | |
| 56 def repo_path(self): | |
| 57 return self._repo_path | |
| 58 | |
| 59 @property | |
| 60 def real_repo_path(self): | |
| 61 """Absolute path of the local repository.""" | |
| 62 return os.path.join(CHECKOUT_ROOT_DIR, self._host, self.repo_path) | |
| 63 | |
| 64 @property | |
| 65 def repo_url(self): | |
| 66 """Url of remote repository which the local repo checks out from.""" | |
| 67 return self._repo_url | |
| 68 | |
| 69 @repo_url.setter | |
| 70 def repo_url(self, repo_url): | |
| 71 self._SetFieldsFromRepoUrl(repo_url) | |
| 72 | |
| 73 def _SetFieldsFromRepoUrl(self, repo_url): | |
|
wrengr
2016/11/08 19:32:37
Why is this factored out as a separate method? why
Sharu Jiang
2016/11/11 00:29:05
Because this needs to be called in __init__, and h
wrengr
2016/11/11 18:38:48
Oh! That's what was up with things before! If that
Sharu Jiang
2016/11/11 23:43:49
According to http://stackoverflow.com/questions/39
| |
| 74 if self._repo_url == repo_url: | |
| 75 return | |
| 76 | |
| 77 self._repo_url = repo_url | |
| 78 if not self._repo_url: | |
| 79 return | |
| 80 | |
| 81 parsed_url = urlparse(repo_url) | |
| 82 self._host = parsed_url.netloc | |
| 83 # Remove the / in the front of path. | |
| 84 self._repo_path = parsed_url.path[1:] | |
| 85 | |
| 86 self._CloneOrUpdateRepoIfNeeded() | |
| 87 | |
| 88 def _CloneOrUpdateRepoIfNeeded(self): | |
| 89 """Clones repo, or update it if it didn't got updated before.""" | |
| 90 if self.repo_url in LocalGitRepository._updated_repos: | |
| 91 return | |
| 92 | |
| 93 with LocalGitRepository.lock: | |
| 94 # Clone the repo if needed. | |
| 95 if not os.path.exists(self.real_repo_path): | |
| 96 try: | |
| 97 subprocess.check_call(['git', 'clone', | |
| 98 self.repo_url, self.real_repo_path]) | |
| 99 except subprocess.CalledProcessError as e: # pragma: no cover. | |
| 100 logging.error('Exception while cloning %s: %s', self.repo_url, e) | |
| 101 return | |
| 102 # Update repo if it's already cloned. | |
| 103 else: | |
| 104 try: | |
| 105 # Disable verbose of cd and git pull. | |
| 106 with open(os.devnull, 'w') as null_handle: | |
| 107 subprocess.check_call( | |
| 108 'cd %s && git pull' % self.real_repo_path, | |
| 109 stdout=null_handle, stderr=null_handle, shell=True) | |
| 110 except subprocess.CalledProcessError as e: # pragma: no cover. | |
| 111 logging.error('Exception while updating %s: %s', self.repo_path, e) | |
| 112 return | |
| 113 | |
| 114 LocalGitRepository._updated_repos.add(self.repo_url) | |
| 115 | |
| 116 | |
| 117 def _GetFinalCommand(self, command, format_date=False): | |
| 118 # Change local time to utc time. | |
| 119 if format_date: | |
| 120 command = 'TZ=UTC %s --date=format-local:"%s"' % ( | |
|
stgao
2016/11/08 21:56:36
So it works again?
Sharu Jiang
2016/11/11 00:29:06
The only case it doesn't work is 'git blame' + '--
| |
| 121 command, local_git_parsers.DATETIME_FORMAT) | |
| 122 return 'cd %s && %s' % (self.real_repo_path, command) | |
| 123 | |
| 124 def GetChangeLog(self, revision): | |
| 125 """Returns the change log of the given revision.""" | |
| 126 command = ('git log --pretty=format:"%s" --max-count=1 --raw ' | |
| 127 '--no-abbrev %s' % (_CHANGELOG_FORMAT_STRING, | |
| 128 ConvertRemoteCommitToLocal(revision))) | |
| 129 output = script_util.GetCommandOutput(self._GetFinalCommand(command, True)) | |
| 130 return self.changelog_parser(output, self.repo_url) | |
| 131 | |
| 132 def GetChangeLogs(self, start_revision, end_revision): # pylint: disable=W | |
| 133 """Returns change log list in (start_revision, end_revision].""" | |
| 134 command = ('git log --pretty=format:"%s" --raw --no-abbrev %s' % ( | |
| 135 _CHANGELOGS_FORMAT_STRING, | |
| 136 '%s..%s' % (ConvertRemoteCommitToLocal(start_revision), | |
| 137 ConvertRemoteCommitToLocal(end_revision)))) | |
| 138 output = script_util.GetCommandOutput(self._GetFinalCommand(command, True)) | |
| 139 return self.changelogs_parser(output, self.repo_url) | |
| 140 | |
| 141 def GetChangeDiff(self, revision, path=None): # pylint: disable=W | |
| 142 """Returns the diff of the given revision.""" | |
| 143 command = ('git log --format="" --max-count=1 %s' % | |
| 144 ConvertRemoteCommitToLocal(revision)) | |
| 145 if path: | |
| 146 command += ' -p %s' % path | |
| 147 output = script_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 148 return self.diff_parser(output) | |
| 149 | |
| 150 def GetBlame(self, path, revision): | |
| 151 """Returns blame of the file at ``path`` of the given revision.""" | |
| 152 command = 'git blame --incremental %s %s' % ( | |
| 153 path, ConvertRemoteCommitToLocal(revision)) | |
| 154 output = script_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 155 return self.blame_parser(output, path, revision) | |
| 156 | |
| 157 def GetSource(self, path, revision): | |
| 158 """Returns source code of the file at ``path`` of the given revision.""" | |
| 159 # Check whether the requested file exist or not. | |
| 160 if not os.path.isfile(os.path.join(self.real_repo_path, path)): | |
|
stgao
2016/11/08 21:56:36
What if the file got deleted after some revision?
Sharu Jiang
2016/11/11 00:29:06
good catch, this should be removed since if the fi
stgao
2016/11/11 01:07:59
This seems unexpected compared to the Gitiles.
So
Sharu Jiang
2016/11/11 23:52:38
It is possible to get the content of a deleted fil
| |
| 161 return None | |
| 162 command = 'git show %s:%s' % (ConvertRemoteCommitToLocal(revision), path) | |
| 163 output = script_util.GetCommandOutput(self._GetFinalCommand(command)) | |
| 164 return self.source_parser(output) | |
| OLD | NEW |