Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: tools/auto_bisect/source_control.py

Issue 661803003: Revert of Refactor source_control.py and add a test. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/auto_bisect/bisect_results.py ('k') | tools/auto_bisect/source_control_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """This module contains functions for performing source control operations.""" 5 """This module contains the SourceControl class and related functions."""
6 6
7 import os 7 import os
8 8
9 import bisect_utils 9 import bisect_utils
10 10
11 CROS_VERSION_PATTERN = 'new version number from %s' 11 CROS_VERSION_PATTERN = 'new version number from %s'
12 12
13 13
14 def IsInGitRepository(): 14 def DetermineAndCreateSourceControl(opts):
15 output, _ = bisect_utils.RunGit(['rev-parse', '--is-inside-work-tree']) 15 """Attempts to determine the underlying source control workflow and returns
16 return output.strip() == 'true' 16 a SourceControl object.
17
18
19 def SyncToRevisionWithGClient(revision):
20 """Uses gclient to sync to the specified revision.
21
22 This is like running gclient sync --revision <revision>.
23
24 Args:
25 revision: A git SHA1 hash or SVN revision number (depending on workflow).
26 17
27 Returns: 18 Returns:
28 The return code of the call. 19 An instance of a SourceControl object, or None if the current workflow
20 is unsupported.
29 """ 21 """
30 return bisect_utils.RunGClient( 22 (output, _) = bisect_utils.RunGit(['rev-parse', '--is-inside-work-tree'])
31 ['sync', '--verbose', '--reset', '--force', 23
32 '--delete_unversioned_trees', '--nohooks', '--revision', revision]) 24 if output.strip() == 'true':
33 25 return GitSourceControl(opts)
34 26
35 def SyncToRevisionWithRepo(timestamp): 27 return None
36 return bisect_utils.RunRepoSyncAtTimestamp(timestamp) 28
37 29
38 30 # TODO(qyearsley): Almost all of the methods below could be top-level functions
39 def GetRevisionList(end_revision_hash, start_revision_hash, cwd=None): 31 # (or class methods). Refactoring may make this simpler.
40 """Retrieves a list of git commit hashes in a range. 32 # pylint: disable=R0201
41 33 class SourceControl(object):
42 Args: 34 """SourceControl is an abstraction over the source control system."""
43 end_revision_hash: The SHA1 for the end of the range, inclusive. 35
44 start_revision_hash: The SHA1 for the beginning of the range, inclusive. 36 def __init__(self):
45 37 super(SourceControl, self).__init__()
46 Returns: 38
47 A list of the git commit hashes in the range, in reverse time order -- 39 def SyncToRevisionWithGClient(self, revision):
48 that is, starting with |end_revision_hash|. 40 """Uses gclient to sync to the specified revision.
49 """ 41
50 revision_range = '%s..%s' % (start_revision_hash, end_revision_hash) 42 This is like running gclient sync --revision <revision>.
51 cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range] 43
52 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 44 Args:
53 45 revision: A git SHA1 hash or SVN revision number (depending on workflow).
54 revision_hash_list = log_output.split() 46
55 revision_hash_list.append(start_revision_hash) 47 Returns:
56 48 The return code of the call.
57 return revision_hash_list 49 """
58 50 return bisect_utils.RunGClient(['sync', '--verbose', '--reset', '--force',
59 51 '--delete_unversioned_trees', '--nohooks', '--revision', revision])
60 def SyncToRevision(revision, sync_client=None): 52
61 if not sync_client: 53 def SyncToRevisionWithRepo(self, timestamp):
62 _, return_code = bisect_utils.RunGit(['checkout', revision])[1] 54 """Uses the repo command to sync all the underlying git depots to the
63 elif sync_client == 'gclient': 55 specified time.
64 return_code = SyncToRevisionWithGClient(revision) 56
65 elif sync_client == 'repo': 57 Args:
66 return_code = SyncToRevisionWithRepo(revision) 58 timestamp: The Unix timestamp to sync to.
67 else: 59
68 raise NotImplementedError('Unsupported sync_client: "%s"' % sync_client) 60 Returns:
69 61 The return code of the call.
70 return not return_code 62 """
71 63 return bisect_utils.RunRepoSyncAtTimestamp(timestamp)
72 64
73 def ResolveToRevision(revision_to_check, depot, depot_deps_dict, 65
74 search, cwd=None): 66 class GitSourceControl(SourceControl):
75 """Tries to resolve an SVN revision or commit position to a git SHA1. 67 """GitSourceControl is used to query the underlying source control."""
76 68
77 Args: 69 def __init__(self, opts):
78 revision_to_check: The user supplied revision string that may need to be 70 super(GitSourceControl, self).__init__()
79 resolved to a git commit hash. This may be an SVN revision, git commit 71 self.opts = opts
80 position, or a git commit hash. 72
81 depot: The depot (dependency repository) that |revision_to_check| is from. 73 def IsGit(self):
82 depot_deps_dict: A dictionary with information about different depots. 74 return True
83 search: How many revisions forward or backward to search. If the value is 75
84 negative, the function will search backwards chronologically, otherwise 76 def GetRevisionList(self, revision_range_end, revision_range_start, cwd=None):
85 it will search forward. 77 """Retrieves a list of revisions between |revision_range_start| and
86 78 |revision_range_end|.
87 Returns: 79
88 A string containing a git SHA1 hash, otherwise None. 80 Args:
89 """ 81 revision_range_end: The SHA1 for the end of the range.
90 # Android-chrome is git only, so no need to resolve this to anything else. 82 revision_range_start: The SHA1 for the beginning of the range.
91 if depot == 'android-chrome': 83
92 return revision_to_check 84 Returns:
93 85 A list of the revisions between |revision_range_start| and
94 if depot == 'cros': 86 |revision_range_end| (inclusive).
95 return ResolveToRevisionCrOS(revision_to_check, cwd) 87 """
96 88 revision_range = '%s..%s' % (revision_range_start, revision_range_end)
97 # If the given revision can't be parsed as an integer, then it may already 89 cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range]
98 # be a git commit hash.
99 if not bisect_utils.IsStringInt(revision_to_check):
100 return revision_to_check
101
102 depot_svn = 'svn://svn.chromium.org/chrome/trunk/src'
103
104 if depot != 'chromium':
105 depot_svn = depot_deps_dict[depot]['svn']
106 svn_revision = int(revision_to_check)
107 git_revision = None
108
109 if search > 0:
110 search_range = xrange(svn_revision, svn_revision + search, 1)
111 else:
112 search_range = xrange(svn_revision, svn_revision + search, -1)
113
114 for i in search_range:
115 # NOTE: Checking for the git-svn-id footer is for backwards compatibility.
116 # When we can assume that all the revisions we care about are from after
117 # git commit positions started getting added, we don't need to check this.
118 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i)
119 commit_position_pattern = '^Cr-Commit-Position: .*@{#%d}' % i
120 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern,
121 '--grep', commit_position_pattern, 'origin/master']
122 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 90 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
91
92 revision_hash_list = log_output.split()
93 revision_hash_list.append(revision_range_start)
94
95 return revision_hash_list
96
97 def SyncToRevision(self, revision, sync_client=None):
98 """Syncs to the specified revision.
99
100 Args:
101 revision: The revision to sync to.
102 use_gclient: Specifies whether or not we should sync using gclient or
103 just use source control directly.
104
105 Returns:
106 True if successful.
107 """
108
109 if not sync_client:
110 results = bisect_utils.RunGit(['checkout', revision])[1]
111 elif sync_client == 'gclient':
112 results = self.SyncToRevisionWithGClient(revision)
113 elif sync_client == 'repo':
114 results = self.SyncToRevisionWithRepo(revision)
115
116 return not results
117
118 def ResolveToRevision(self, revision_to_check, depot, depot_deps_dict,
119 search, cwd=None):
120 """Tries to resolve an SVN revision or commit position to a git SHA1.
121
122 Args:
123 revision_to_check: The user supplied revision string that may need to be
124 resolved to a git SHA1.
125 depot: The depot the revision_to_check is from.
126 depot_deps_dict: A dictionary with information about different depots.
127 search: The number of changelists to try if the first fails to resolve
128 to a git hash. If the value is negative, the function will search
129 backwards chronologically, otherwise it will search forward.
130
131 Returns:
132 A string containing a git SHA1 hash, otherwise None.
133 """
134 # Android-chrome is git only, so no need to resolve this to anything else.
135 if depot == 'android-chrome':
136 return revision_to_check
137
138 if depot != 'cros':
139 if not bisect_utils.IsStringInt(revision_to_check):
140 return revision_to_check
141
142 depot_svn = 'svn://svn.chromium.org/chrome/trunk/src'
143
144 if depot != 'chromium':
145 depot_svn = depot_deps_dict[depot]['svn']
146
147 svn_revision = int(revision_to_check)
148 git_revision = None
149
150 if search > 0:
151 search_range = xrange(svn_revision, svn_revision + search, 1)
152 else:
153 search_range = xrange(svn_revision, svn_revision + search, -1)
154
155 for i in search_range:
156 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i)
157 commit_position_pattern = '^Cr-Commit-Position: .*@{#%d}' % i
158 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern,
159 '--grep', commit_position_pattern, 'origin/master']
160
161 (log_output, return_code) = bisect_utils.RunGit(cmd, cwd=cwd)
162
163 assert not return_code, 'An error occurred while running'\
164 ' "git %s"' % ' '.join(cmd)
165
166 if not return_code:
167 log_output = log_output.strip()
168
169 if log_output:
170 git_revision = log_output
171
172 break
173
174 return git_revision
175 else:
176 if bisect_utils.IsStringInt(revision_to_check):
177 return int(revision_to_check)
178 else:
179 cwd = os.getcwd()
180 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party',
181 'chromiumos-overlay'))
182 pattern = CROS_VERSION_PATTERN % revision_to_check
183 cmd = ['log', '--format=%ct', '-1', '--grep', pattern]
184
185 git_revision = None
186
187 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
188 if log_output:
189 git_revision = log_output
190 git_revision = int(log_output.strip())
191 os.chdir(cwd)
192
193 return git_revision
194
195 def IsInProperBranch(self):
196 """Confirms they're in the master branch for performing the bisection.
197 This is needed or gclient will fail to sync properly.
198
199 Returns:
200 True if the current branch on src is 'master'
201 """
202 cmd = ['rev-parse', '--abbrev-ref', 'HEAD']
203 log_output = bisect_utils.CheckRunGit(cmd)
123 log_output = log_output.strip() 204 log_output = log_output.strip()
124 205
125 if log_output: 206 return log_output == "master"
126 git_revision = log_output 207
127 break 208 def GetCommitPosition(self, git_revision, cwd=None):
128 209 """Finds git commit postion for the given git hash.
129 return git_revision 210
130 211 This function executes "git footer --position-num <git hash>" command to get
131 212 commit position the given revision.
132 def ResolveToRevisionCrOS(revision_to_check, cwd=None): 213
133 """Return a git commit hash corresponding to the give version or revision. 214 Args:
134 215 git_revision: The git SHA1 to use.
135 TODO(qyearsley): Either verify that this works or delete it. 216 cwd: Working directory to run the command from.
136 """ 217
137 if bisect_utils.IsStringInt(revision_to_check): 218 Returns:
138 return int(revision_to_check) 219 Git commit position as integer or None.
139 220 """
140 cwd = os.getcwd() 221 cmd = ['footers', '--position-num', git_revision]
141 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party', 222 output = bisect_utils.CheckRunGit(cmd, cwd)
142 'chromiumos-overlay')) 223 commit_position = output.strip()
143 pattern = CROS_VERSION_PATTERN % revision_to_check 224
144 cmd = ['log', '--format=%ct', '-1', '--grep', pattern] 225 if bisect_utils.IsStringInt(commit_position):
145 226 return int(commit_position)
146 git_revision = None 227
147 228 return None
148 log_output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 229
149 if log_output: 230 def QueryRevisionInfo(self, revision, cwd=None):
150 git_revision = log_output 231 """Gathers information on a particular revision, such as author's name,
151 git_revision = int(log_output.strip()) 232 email, subject, and date.
152 os.chdir(cwd) 233
153 234 Args:
154 return git_revision 235 revision: Revision you want to gather information on.
155 236
156 237 Returns:
157 def IsInProperBranch(): 238 A dict in the following format:
158 """Checks whether the current branch is "master".""" 239 {
159 cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] 240 'author': %s,
160 log_output = bisect_utils.CheckRunGit(cmd) 241 'email': %s,
161 log_output = log_output.strip() 242 'date': %s,
162 return log_output == 'master' 243 'subject': %s,
163 244 'body': %s,
164 245 }
165 def GetCommitPosition(git_revision, cwd=None): 246 """
166 """Finds git commit postion for the given git hash. 247 commit_info = {}
167 248
168 This function executes "git footer --position-num <git hash>" command to get 249 formats = ['%aN', '%aE', '%s', '%cD', '%b']
169 commit position the given revision. 250 targets = ['author', 'email', 'subject', 'date', 'body']
170 251
171 Args: 252 for i in xrange(len(formats)):
172 git_revision: The git SHA1 to use. 253 cmd = ['log', '--format=%s' % formats[i], '-1', revision]
173 cwd: Working directory to run the command from. 254 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
174 255 commit_info[targets[i]] = output.rstrip()
175 Returns: 256
176 Git commit position as integer or None. 257 return commit_info
177 """ 258
178 cmd = ['footers', '--position-num', git_revision] 259 def CheckoutFileAtRevision(self, file_name, revision, cwd=None):
179 output = bisect_utils.CheckRunGit(cmd, cwd) 260 """Performs a checkout on a file at the given revision.
180 commit_position = output.strip() 261
181 262 Returns:
182 if bisect_utils.IsStringInt(commit_position): 263 True if successful.
183 return int(commit_position) 264 """
184 265 return not bisect_utils.RunGit(
185 return None 266 ['checkout', revision, file_name], cwd=cwd)[1]
186 267
187 268 def RevertFileToHead(self, file_name):
188 def QueryRevisionInfo(revision, cwd=None): 269 """Un-stages a file and resets the file's state to HEAD.
189 """Gathers information on a particular revision, such as author's name, 270
190 email, subject, and date. 271 Returns:
191 272 True if successful.
192 Args: 273 """
193 revision: Revision you want to gather information on; a git commit hash. 274 # Reset doesn't seem to return 0 on success.
194 275 bisect_utils.RunGit(['reset', 'HEAD', file_name])
195 Returns: 276
196 A dict in the following format: 277 return not bisect_utils.RunGit(['checkout', bisect_utils.FILE_DEPS_GIT])[1]
197 { 278
198 'author': %s, 279 def QueryFileRevisionHistory(self, filename, revision_start, revision_end):
199 'email': %s, 280 """Returns a list of commits that modified this file.
200 'date': %s, 281
201 'subject': %s, 282 Args:
202 'body': %s, 283 filename: Name of file.
203 } 284 revision_start: Start of revision range.
204 """ 285 revision_end: End of revision range.
205 commit_info = {} 286
206 287 Returns:
207 formats = ['%aN', '%aE', '%s', '%cD', '%b'] 288 Returns a list of commits that touched this file.
208 targets = ['author', 'email', 'subject', 'date', 'body'] 289 """
209 290 cmd = ['log', '--format=%H', '%s~1..%s' % (revision_start, revision_end),
210 for i in xrange(len(formats)): 291 '--', filename]
211 cmd = ['log', '--format=%s' % formats[i], '-1', revision] 292 output = bisect_utils.CheckRunGit(cmd)
212 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 293
213 commit_info[targets[i]] = output.rstrip() 294 return [o for o in output.split('\n') if o]
214
215 return commit_info
216
217
218 def CheckoutFileAtRevision(file_name, revision, cwd=None):
219 """Performs a checkout on a file at the given revision.
220
221 Returns:
222 True if successful.
223 """
224 command = ['checkout', revision, file_name]
225 _, return_code = bisect_utils.RunGit(command, cwd=cwd)
226 return not return_code
227
228
229 def RevertFileToHead(file_name):
230 """Un-stages a file and resets the file's state to HEAD.
231
232 Returns:
233 True if successful.
234 """
235 # Reset doesn't seem to return 0 on success.
236 bisect_utils.RunGit(['reset', 'HEAD', file_name])
237 _, return_code = bisect_utils.RunGit(
238 ['checkout', bisect_utils.FILE_DEPS_GIT])
239 return not return_code
240
241
242 def QueryFileRevisionHistory(filename, revision_start, revision_end):
243 """Returns a list of commits that modified this file.
244
245 Args:
246 filename: Name of file.
247 revision_start: Start of revision range (inclusive).
248 revision_end: End of revision range.
249
250 Returns:
251 Returns a list of commits that touched this file.
252 """
253 cmd = [
254 'log',
255 '--format=%H',
256 '%s~1..%s' % (revision_start, revision_end),
257 '--',
258 filename,
259 ]
260 output = bisect_utils.CheckRunGit(cmd)
261 lines = output.split('\n')
262 return [o for o in lines if o]
263
OLDNEW
« no previous file with comments | « tools/auto_bisect/bisect_results.py ('k') | tools/auto_bisect/source_control_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698