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 |