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 """This module contains the SourceControl class and related functions.""" | |
| 6 | |
| 7 import os | |
| 8 | |
| 9 from . import bisect_utils | |
| 10 | |
| 11 CROS_VERSION_PATTERN = 'new version number from %s' | |
| 12 | |
| 13 | |
| 14 def DetermineAndCreateSourceControl(opts): | |
| 15 """Attempts to determine the underlying source control workflow and returns | |
| 16 a SourceControl object. | |
| 17 | |
| 18 Returns: | |
| 19 An instance of a SourceControl object, or None if the current workflow | |
| 20 is unsupported. | |
| 21 """ | |
| 22 (output, _) = bisect_utils.RunGit(['rev-parse', '--is-inside-work-tree']) | |
| 23 | |
| 24 if output.strip() == 'true': | |
| 25 return GitSourceControl(opts) | |
| 26 | |
| 27 return None | |
| 28 | |
| 29 | |
| 30 class SourceControl(object): | |
|
shatch
2014/07/25 16:28:50
Not for this CL, but probably ok to kill the abstr
| |
| 31 """SourceControl is an abstraction over the source control system.""" | |
| 32 | |
| 33 def __init__(self): | |
| 34 super(SourceControl, self).__init__() | |
| 35 | |
| 36 def SyncToRevisionWithGClient(self, revision): | |
| 37 """Uses gclient to sync to the specified revision. | |
| 38 | |
| 39 ie. gclient sync --revision <revision> | |
| 40 | |
| 41 Args: | |
| 42 revision: The git SHA1 or svn CL (depending on workflow). | |
| 43 | |
| 44 Returns: | |
| 45 The return code of the call. | |
| 46 """ | |
| 47 return bisect_utils.RunGClient(['sync', '--verbose', '--reset', '--force', | |
| 48 '--delete_unversioned_trees', '--nohooks', '--revision', revision]) | |
| 49 | |
| 50 def SyncToRevisionWithRepo(self, timestamp): | |
| 51 """Uses repo to sync all the underlying git depots to the specified | |
| 52 time. | |
| 53 | |
| 54 Args: | |
| 55 timestamp: The unix timestamp to sync to. | |
| 56 | |
| 57 Returns: | |
| 58 The return code of the call. | |
| 59 """ | |
| 60 return bisect_utils.RunRepoSyncAtTimestamp(timestamp) | |
| 61 | |
| 62 | |
| 63 class GitSourceControl(SourceControl): | |
| 64 """GitSourceControl is used to query the underlying source control.""" | |
| 65 | |
| 66 def __init__(self, opts): | |
| 67 super(GitSourceControl, self).__init__() | |
| 68 self.opts = opts | |
| 69 | |
| 70 def IsGit(self): | |
| 71 return True | |
| 72 | |
| 73 def GetRevisionList(self, revision_range_end, revision_range_start, cwd=None): | |
| 74 """Retrieves a list of revisions between |revision_range_start| and | |
| 75 |revision_range_end|. | |
| 76 | |
| 77 Args: | |
| 78 revision_range_end: The SHA1 for the end of the range. | |
| 79 revision_range_start: The SHA1 for the beginning of the range. | |
| 80 | |
| 81 Returns: | |
| 82 A list of the revisions between |revision_range_start| and | |
| 83 |revision_range_end| (inclusive). | |
| 84 """ | |
| 85 revision_range = '%s..%s' % (revision_range_start, revision_range_end) | |
| 86 cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range] | |
| 87 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) | |
| 88 | |
| 89 revision_hash_list = log_output.split() | |
| 90 revision_hash_list.append(revision_range_start) | |
| 91 | |
| 92 return revision_hash_list | |
| 93 | |
| 94 def SyncToRevision(self, revision, sync_client=None): | |
| 95 """Syncs to the specified revision. | |
| 96 | |
| 97 Args: | |
| 98 revision: The revision to sync to. | |
| 99 use_gclient: Specifies whether or not we should sync using gclient or | |
| 100 just use source control directly. | |
| 101 | |
| 102 Returns: | |
| 103 True if successful. | |
| 104 """ | |
| 105 | |
| 106 if not sync_client: | |
| 107 results = bisect_utils.RunGit(['checkout', revision])[1] | |
| 108 elif sync_client == 'gclient': | |
| 109 results = self.SyncToRevisionWithGClient(revision) | |
| 110 elif sync_client == 'repo': | |
| 111 results = self.SyncToRevisionWithRepo(revision) | |
| 112 | |
| 113 return not results | |
| 114 | |
| 115 def ResolveToRevision(self, revision_to_check, depot, depot_deps_dict, | |
| 116 search, cwd=None): | |
| 117 """If an SVN revision is supplied, try to resolve it to a git SHA1. | |
| 118 | |
| 119 Args: | |
| 120 revision_to_check: The user supplied revision string that may need to be | |
| 121 resolved to a git SHA1. | |
| 122 depot: The depot the revision_to_check is from. | |
| 123 depot_deps_dict: A dictionary with information about different depots. | |
| 124 search: The number of changelists to try if the first fails to resolve | |
| 125 to a git hash. If the value is negative, the function will search | |
| 126 backwards chronologically, otherwise it will search forward. | |
| 127 | |
| 128 Returns: | |
| 129 A string containing a git SHA1 hash, otherwise None. | |
| 130 """ | |
| 131 # Android-chrome is git only, so no need to resolve this to anything else. | |
| 132 if depot == 'android-chrome': | |
| 133 return revision_to_check | |
| 134 | |
| 135 if depot != 'cros': | |
| 136 if not bisect_utils.IsStringInt(revision_to_check): | |
| 137 return revision_to_check | |
| 138 | |
| 139 depot_svn = 'svn://svn.chromium.org/chrome/trunk/src' | |
| 140 | |
| 141 if depot != 'chromium': | |
| 142 depot_svn = depot_deps_dict[depot]['svn'] | |
| 143 | |
| 144 svn_revision = int(revision_to_check) | |
| 145 git_revision = None | |
| 146 | |
| 147 if search > 0: | |
| 148 search_range = xrange(svn_revision, svn_revision + search, 1) | |
| 149 else: | |
| 150 search_range = xrange(svn_revision, svn_revision + search, -1) | |
| 151 | |
| 152 for i in search_range: | |
| 153 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) | |
| 154 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, | |
| 155 'origin/master'] | |
| 156 | |
| 157 (log_output, return_code) = bisect_utils.RunGit(cmd, cwd=cwd) | |
| 158 | |
| 159 assert not return_code, 'An error occurred while running'\ | |
| 160 ' "git %s"' % ' '.join(cmd) | |
| 161 | |
| 162 if not return_code: | |
| 163 log_output = log_output.strip() | |
| 164 | |
| 165 if log_output: | |
| 166 git_revision = log_output | |
| 167 | |
| 168 break | |
| 169 | |
| 170 return git_revision | |
| 171 else: | |
| 172 if bisect_utils.IsStringInt(revision_to_check): | |
| 173 return int(revision_to_check) | |
| 174 else: | |
| 175 cwd = os.getcwd() | |
| 176 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party', | |
| 177 'chromiumos-overlay')) | |
| 178 pattern = CROS_VERSION_PATTERN % revision_to_check | |
| 179 cmd = ['log', '--format=%ct', '-1', '--grep', pattern] | |
| 180 | |
| 181 git_revision = None | |
| 182 | |
| 183 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) | |
| 184 if log_output: | |
| 185 git_revision = log_output | |
| 186 git_revision = int(log_output.strip()) | |
| 187 os.chdir(cwd) | |
| 188 | |
| 189 return git_revision | |
| 190 | |
| 191 def IsInProperBranch(self): | |
| 192 """Confirms they're in the master branch for performing the bisection. | |
| 193 This is needed or gclient will fail to sync properly. | |
| 194 | |
| 195 Returns: | |
| 196 True if the current branch on src is 'master' | |
| 197 """ | |
| 198 cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] | |
| 199 log_output = bisect_utils.CheckRunGit(cmd) | |
| 200 log_output = log_output.strip() | |
| 201 | |
| 202 return log_output == "master" | |
| 203 | |
| 204 def SVNFindRev(self, revision, cwd=None): | |
| 205 """Maps directly to the 'git svn find-rev' command. | |
| 206 | |
| 207 Args: | |
| 208 revision: The git SHA1 to use. | |
| 209 | |
| 210 Returns: | |
| 211 An integer changelist #, otherwise None. | |
| 212 """ | |
| 213 | |
| 214 cmd = ['svn', 'find-rev', revision] | |
| 215 | |
| 216 output = bisect_utils.CheckRunGit(cmd, cwd) | |
| 217 svn_revision = output.strip() | |
| 218 | |
| 219 if bisect_utils.IsStringInt(svn_revision): | |
| 220 return int(svn_revision) | |
| 221 | |
| 222 return None | |
| 223 | |
| 224 def QueryRevisionInfo(self, revision, cwd=None): | |
| 225 """Gathers information on a particular revision, such as author's name, | |
| 226 email, subject, and date. | |
| 227 | |
| 228 Args: | |
| 229 revision: Revision you want to gather information on. | |
| 230 Returns: | |
| 231 A dict in the following format: | |
| 232 { | |
| 233 'author': %s, | |
| 234 'email': %s, | |
| 235 'date': %s, | |
| 236 'subject': %s, | |
| 237 'body': %s, | |
| 238 } | |
| 239 """ | |
| 240 commit_info = {} | |
| 241 | |
| 242 formats = ['%cN', '%cE', '%s', '%cD', '%b'] | |
| 243 targets = ['author', 'email', 'subject', 'date', 'body'] | |
| 244 | |
| 245 for i in xrange(len(formats)): | |
| 246 cmd = ['log', '--format=%s' % formats[i], '-1', revision] | |
| 247 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) | |
| 248 commit_info[targets[i]] = output.rstrip() | |
| 249 | |
| 250 return commit_info | |
| 251 | |
| 252 def CheckoutFileAtRevision(self, file_name, revision, cwd=None): | |
| 253 """Performs a checkout on a file at the given revision. | |
| 254 | |
| 255 Returns: | |
| 256 True if successful. | |
| 257 """ | |
| 258 return not bisect_utils.RunGit( | |
| 259 ['checkout', revision, file_name], cwd=cwd)[1] | |
| 260 | |
| 261 def RevertFileToHead(self, file_name): | |
| 262 """Unstages a file and returns it to HEAD. | |
| 263 | |
| 264 Returns: | |
| 265 True if successful. | |
| 266 """ | |
| 267 # Reset doesn't seem to return 0 on success. | |
| 268 bisect_utils.RunGit(['reset', 'HEAD', file_name]) | |
| 269 | |
| 270 return not bisect_utils.RunGit(['checkout', bisect_utils.FILE_DEPS_GIT])[1] | |
| 271 | |
| 272 def QueryFileRevisionHistory(self, filename, revision_start, revision_end): | |
| 273 """Returns a list of commits that modified this file. | |
| 274 | |
| 275 Args: | |
| 276 filename: Name of file. | |
| 277 revision_start: Start of revision range. | |
| 278 revision_end: End of revision range. | |
| 279 | |
| 280 Returns: | |
| 281 Returns a list of commits that touched this file. | |
| 282 """ | |
| 283 cmd = ['log', '--format=%H', '%s~1..%s' % (revision_start, revision_end), | |
| 284 filename] | |
| 285 output = bisect_utils.CheckRunGit(cmd) | |
| 286 | |
| 287 return [o for o in output.split('\n') if o] | |
| OLD | NEW |