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

Side by Side Diff: checkout.py

Issue 6020010: Destroy svn_utils.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/commit-queue
Patch Set: Created 10 years 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | svn_utils.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 # coding=utf8 1 # coding=utf8
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 """Manages a project checkout. 5 """Manages a project checkout.
6 6
7 Includes support for svn, git-svn and git. 7 Includes support for svn, git-svn and git.
8 """ 8 """
9 9
10 import logging 10 import logging
11 import os 11 import os
12 import re 12 import re
13 import subprocess 13 import subprocess
14 import tempfile 14 import tempfile
15 15
16 # From depot_tools
17 import gclient_utils
18 import scm
19
16 import patch 20 import patch
17 import subprocess2 21 import subprocess2
18 import svn_utils
19 22
20 23
21 def get_code_review_setting(path, key, 24 def get_code_review_setting(path, key,
22 codereview_settings_file='codereview.settings'): 25 codereview_settings_file='codereview.settings'):
23 """Parses codereview.settings and return the value for the key if present. 26 """Parses codereview.settings and return the value for the key if present.
24 27
25 Don't cache the values in case the file is changed.""" 28 Don't cache the values in case the file is changed."""
26 settings = {} 29 settings = {}
27 try: 30 try:
28 settings_file = open(os.path.join(path, codereview_settings_file), 'r') 31 settings_file = open(os.path.join(path, codereview_settings_file), 'r')
(...skipping 16 matching lines...) Expand all
45 class CheckoutBase(object): 48 class CheckoutBase(object):
46 def __init__(self, root_dir, project_name): 49 def __init__(self, root_dir, project_name):
47 self.root_dir = root_dir 50 self.root_dir = root_dir
48 self.project_name = project_name 51 self.project_name = project_name
49 self.project_path = os.path.join(self.root_dir, self.project_name) 52 self.project_path = os.path.join(self.root_dir, self.project_name)
50 53
51 def get_settings(self, key): 54 def get_settings(self, key):
52 return get_code_review_setting(self.project_path, key) 55 return get_code_review_setting(self.project_path, key)
53 56
54 57
55 class SvnCheckout(CheckoutBase): 58 class SvnMixIn(object):
59 """MixIn class to add svn commands common to both svn and git-svn clients."""
60 # These members need to be set by the subclass.
61 commit_user = None
62 commit_pwd = None
63 svn_url = None
64 project_path = None
65
66 def _check_call_svn(self, args, **kwargs):
67 """Runs svn and throws an exception if the command failed."""
68 kwargs.setdefault('cwd', self.project_path)
69 return subprocess2.check_call(
70 ['svn'] + args + ['--no-auth-cache', '--non-interactive'], **kwargs)
71
72 def _capture_svn(self, args, **kwargs):
Dirk Pranke 2010/12/22 23:18:10 This is only used by SvnCheckout, right? If so, I
M-A Ruel 2010/12/23 17:19:49 Ok, I'll move it in a later change otherwise it cr
73 """Runs svn and throws an exception if the command failed.
74
75 Returns the output.
76 """
77 kwargs.setdefault('cwd', self.project_path)
78 if self.commit_user:
79 args = args + [
80 '--username', self.commit_user, '--password', self.commit_pwd]
81 cmd = ['svn'] + args + ['--no-auth-cache', '--non-interactive']
82 return subprocess2.check_capture(cmd, **kwargs)
83
84 @staticmethod
85 def _parse_svn_info(output, key):
86 """Returns value for key from svn info output.
87
88 Case insensitive.
89 """
90 values = {}
91 for line in output.splitlines(False):
92 if not line:
93 continue
94 k, v = line.split(':', 1)
95 k = k.strip().lower()
96 v = v.strip()
97 assert not k in values
98 values[k] = v
99 return values.get(key, None)
100
101 def _update_committer(self, revision, new_author):
102 """Changes the author of a commit a posteriori.
103
104 This is necessary since the actual commit is done with a "commit-bot"
105 credential but the original patch author needs to be assigned authorship
106 of the revision.
107 """
108 self._check_call_svn(
109 ['propset', '--revprop', 'svn:author',
110 '-r', revision,
111 new_author,
112 '--username', self.commit_user,
113 '--password', self.commit_pwd,
114 self.svn_url])
115
116
117 class SvnCheckout(CheckoutBase, SvnMixIn):
56 """Manages a subversion checkout. 118 """Manages a subversion checkout.
57 119
58 Commit is not fully implemented yet. Reimplementing all the commands is 120 Commit is not fully implemented yet. Reimplementing all the commands is
59 slightly complex, svn add with history needs to be done with svn 121 slightly complex, svn add with history needs to be done with svn
60 copy/rename/move and the situation quickly becomes harder when directories are 122 copy/rename/move and the situation quickly becomes harder when directories are
61 moved. 123 moved.
62 """ 124 """
63 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url): 125 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url):
64 super(SvnCheckout, self).__init__(root_dir, project_name) 126 super(SvnCheckout, self).__init__(root_dir, project_name)
65 self.commit_user = commit_user 127 self.commit_user = commit_user
66 self.commit_pwd = commit_pwd 128 self.commit_pwd = commit_pwd
67 self.svn_url = svn_url 129 self.svn_url = svn_url
68 assert bool(self.commit_user) == bool(self.commit_pwd) 130 assert bool(self.commit_user) == bool(self.commit_pwd)
69 assert bool(self.svn_url) 131 assert bool(self.svn_url)
70 132
71 def prepare(self): 133 def prepare(self):
72 """Creates the initial checkouts for the repo.""" 134 """Creates the initial checkouts for the repo."""
73 # Will checkout if the directory is not present. 135 # Will checkout if the directory is not present.
74 logging.info('Checking out %s in %s' % 136 logging.info('Checking out %s in %s' %
75 (self.project_name, self.project_path)) 137 (self.project_name, self.project_path))
76 return svn_utils.revert( 138 return self._revert()
77 self.svn_url,
78 self.project_path,
79 self.commit_user,
80 self.commit_pwd)
81 139
82 def apply_patch(self, patch_data): 140 def apply_patch(self, patch_data):
83 """Applies a patch.""" 141 """Applies a patch."""
84 try: 142 try:
85 patch.apply_patch(self.project_path, patch_data) 143 patch.apply_patch(self.project_path, patch_data)
86 return True 144 return True
87 except subprocess.CalledProcessError, e: 145 except subprocess.CalledProcessError, e:
88 if e.returncode == 1: 146 if e.returncode == 1:
89 return False 147 return False
90 raise 148 raise
91 149
92 def commit(self, commit_message, user): 150 def commit(self, commit_message, user):
93 """Commits a patch.""" 151 """Commits a patch."""
94 logging.info('Committing patch for %s' % user) 152 logging.info('Committing patch for %s' % user)
95 assert self.commit_user 153 assert self.commit_user
96 assert self.commit_pwd 154 assert self.commit_pwd
97 handle, commit_filename = tempfile.mkstemp(text=True) 155 handle, commit_filename = tempfile.mkstemp(text=True)
98 os.write(handle, commit_message) 156 os.write(handle, commit_message)
99 os.close(handle) 157 os.close(handle)
100 try: 158 try:
101 output = svn_utils.capture_svn([ 159 output = self._capture_svn(['commit', '--file', commit_filename])
102 'commit',
103 '--username', self.commit_user,
104 '--password', self.commit_pwd,
105 '--file', commit_filename],
106 cwd=self.project_path)
107 revision = re.compile( 160 revision = re.compile(
108 r'.*?\nCommitted revision (\d+)', 161 r'.*?\nCommitted revision (\d+)',
109 re.DOTALL).match(output).group(1) 162 re.DOTALL).match(output).group(1)
110 # Fix the committer. 163 # Fix the committer.
111 svn_utils.update_committer( 164 self._update_committer(revision, user)
112 self.svn_url, revision, self.commit_user, self.commit_pwd, user,
113 self.project_path)
114 finally: 165 finally:
115 os.remove(commit_filename) 166 os.remove(commit_filename)
116 return int(revision) 167 return int(revision)
117 168
169 def _revert(self):
170 """Reverts local modifications or checks out if the directory is not
171 present.
172 """
173 flags = ['--ignore-externals']
174 if not os.path.isdir(self.project_path):
175 logging.info('Directory %s is not present, checking it out.' %
176 self.project_path)
177 self._check_call_svn(['checkout', self.svn_url, self.project_path] +
178 flags, cwd=None)
179 else:
180 for file_status in scm.SVN.CaptureStatus(self.project_path):
181 file_path = os.path.join(self.project_path, file_status[1])
182 if file_status[0][0] == 'X':
183 # Ignore externals.
184 logging.info('Ignoring external %s' % file_path)
185 continue
186
187 logging.info('%s%s' % (file_status[0], file_status[1]))
188
189 if file_status[0].isspace():
190 raise EnvironmentError(
191 'No idea what is the status of %s.\n'
192 'You just found a bug in gclient, please ping '
193 'maruel@chromium.org ASAP!' % file_path)
194
195 # svn revert is really stupid. It fails on inconsistent line-endings,
196 # on switched directories, etc. So take no chance and delete everything!
197 try:
198 if not os.path.exists(file_path):
199 pass
200 elif os.path.isfile(file_path) or os.path.islink(file_path):
201 logging.info('os.remove(%s)' % file_path)
202 os.remove(file_path)
203 elif os.path.isdir(file_path):
204 logging.info('gclient_utils.RemoveDirectory(%s)' % file_path)
205 gclient_utils.RemoveDirectory(file_path)
206 else:
207 logging.error('no idea what is %s.\nYou just found a bug in gclient'
208 ', please ping maruel@chromium.org ASAP!' % file_path)
209 except EnvironmentError:
210 logging.error('Failed to remove %s.' % file_path)
211
212 # Revive files that were deleted above.
213 self._check_call_svn(['update', '--force'] + flags)
214
215 out = self._capture_svn(['info', '.'])
216 return int(self._parse_svn_info(out, 'revision'))
217
118 218
119 class GitCheckoutBase(CheckoutBase): 219 class GitCheckoutBase(CheckoutBase):
120 """Base class for git checkout. Not to be used as-is.""" 220 """Base class for git checkout. Not to be used as-is."""
121 def __init__(self, root_dir, project_name, remote_branch): 221 def __init__(self, root_dir, project_name, remote_branch):
122 super(GitCheckoutBase, self).__init__(root_dir, project_name) 222 super(GitCheckoutBase, self).__init__(root_dir, project_name)
123 # There is no reason to not hardcode it. 223 # There is no reason to not hardcode it.
124 self.remote = 'origin' 224 self.remote = 'origin'
125 self.remote_branch = remote_branch 225 self.remote_branch = remote_branch
126 self.working_branch = 'working_branch' 226 self.working_branch = 'working_branch'
127 227
(...skipping 22 matching lines...) Expand all
150 def _call_git(self, args, **kwargs): 250 def _call_git(self, args, **kwargs):
151 """Like check_call but doesn't throw on failure.""" 251 """Like check_call but doesn't throw on failure."""
152 kwargs.setdefault('cwd', self.project_path) 252 kwargs.setdefault('cwd', self.project_path)
153 return subprocess2.call(['git'] + args, **kwargs) 253 return subprocess2.call(['git'] + args, **kwargs)
154 254
155 def _check_capture_git(self, args, **kwargs): 255 def _check_capture_git(self, args, **kwargs):
156 kwargs.setdefault('cwd', self.project_path) 256 kwargs.setdefault('cwd', self.project_path)
157 return subprocess2.check_capture(['git'] + args, **kwargs) 257 return subprocess2.check_capture(['git'] + args, **kwargs)
158 258
159 259
160 class GitSvnCheckoutBase(GitCheckoutBase): 260 class GitSvnCheckoutBase(GitCheckoutBase, SvnMixIn):
161 """Base class for git-svn checkout. Not to be used as-is.""" 261 """Base class for git-svn checkout. Not to be used as-is."""
162 def __init__(self, 262 def __init__(self,
163 root_dir, project_name, remote_branch, 263 root_dir, project_name, remote_branch,
164 commit_user, commit_pwd, 264 commit_user, commit_pwd,
165 svn_repo, trunk): 265 svn_url, trunk):
166 """trunk is optional.""" 266 """trunk is optional."""
167 super(GitSvnCheckoutBase, self).__init__( 267 super(GitSvnCheckoutBase, self).__init__(
168 root_dir, project_name + '.git', remote_branch) 268 root_dir, project_name + '.git', remote_branch)
169 self.commit_user = commit_user 269 self.commit_user = commit_user
170 self.commit_pwd = commit_pwd 270 self.commit_pwd = commit_pwd
171 self.svn_repo = svn_repo 271 # svn_url in this case is the root of the svn repository.
272 self.svn_url = svn_url
172 self.trunk = trunk 273 self.trunk = trunk
173 274
174 def prepare(self): 275 def prepare(self):
175 """Resets the git repository in a clean state.""" 276 """Resets the git repository in a clean state."""
176 self._check_call_git(['checkout', 'master', '--force']) 277 self._check_call_git(['checkout', 'master', '--force'])
177 self._check_call_git(['svn', 'rebase']) 278 self._check_call_git(['svn', 'rebase'])
178 self._call_git(['branch', '-D', self.working_branch]) 279 self._call_git(['branch', '-D', self.working_branch])
179 return int(self._git_svn_info('revision')) 280 return int(self._git_svn_info('revision'))
180 281
181 def _git_svn_info(self, key): 282 def _git_svn_info(self, key):
182 return svn_utils.parse_svn_info( 283 return self._parse_svn_info(
183 self._check_capture_git(['svn', 'info']), key) 284 self._check_capture_git(['svn', 'info']), key)
184 285
185 def commit(self, commit_message, user): 286 def commit(self, commit_message, user):
186 """Commits a patch.""" 287 """Commits a patch."""
187 logging.info('Committing patch for %s' % user) 288 logging.info('Committing patch for %s' % user)
188 # Fix the commit message. 289 # Fix the commit message.
189 super(GitSvnCheckoutBase, self).commit(commit_message, user) 290 super(GitSvnCheckoutBase, self).commit(commit_message, user)
190 # Commit with git svn dcommit, then use svn directly to update the 291 # Commit with git svn dcommit, then use svn directly to update the
191 # committer on the revision. 292 # committer on the revision.
192 self._check_call_git_svn(['dcommit', '--rmdir', '--find-copies-harder']) 293 self._check_call_git_svn(['dcommit', '--rmdir', '--find-copies-harder'])
193 revision = int(self._git_svn_info('revision')) 294 revision = int(self._git_svn_info('revision'))
194 # Fix the committer. 295 # Fix the committer.
195 svn_utils.update_committer( 296 self._update_committer(revision, user)
196 self.svn_repo, revision, self.commit_user, self.commit_pwd, user,
197 self.project_path)
198 return revision 297 return revision
199 298
200 def _cache_svn_auth(self): 299 def _cache_svn_auth(self):
201 """Caches the svn credentials. It is necessary since git-svn doesn't prompt 300 """Caches the svn credentials. It is necessary since git-svn doesn't prompt
202 for it.""" 301 for it."""
203 if not self.commit_user: 302 if not self.commit_user:
204 return 303 return
205 logging.info('Caching svn credentials for %s' % self.commit_user) 304 logging.info('Caching svn credentials for %s' % self.commit_user)
206 subprocess2.check_call( 305 subprocess2.check_call(
207 ['svn', 'ls', self.svn_repo, 306 ['svn', 'ls', self.svn_url,
208 '--username', self.commit_user, 307 '--username', self.commit_user,
209 '--password', self.commit_pwd, 308 '--password', self.commit_pwd,
210 '--non-interactive']) 309 '--non-interactive'])
211 310
212 def _check_call_git_svn(self, args, **kwargs): 311 def _check_call_git_svn(self, args, **kwargs):
213 """Handles svn authentication while calling git svn.""" 312 """Handles svn authentication while calling git svn."""
214 args = ['svn'] + args 313 args = ['svn'] + args
215 if self.commit_user: 314 if self.commit_user:
216 args = args + ['--username', self.commit_user, '--no-auth-cache'] 315 args = args + ['--username', self.commit_user, '--no-auth-cache']
217 return self._check_call_git(args, stdin=self.commit_pwd, **kwargs) 316 return self._check_call_git(args, stdin=self.commit_pwd, **kwargs)
218 317
219 318
220 class GitSvnPremadeCheckout(GitSvnCheckoutBase): 319 class GitSvnPremadeCheckout(GitSvnCheckoutBase):
221 """Manages a git-svn clone made out from an initial git-svn seed. 320 """Manages a git-svn clone made out from an initial git-svn seed.
222 321
223 This class is very similar to GitSvnCheckout but is faster to bootstrap 322 This class is very similar to GitSvnCheckout but is faster to bootstrap
224 because it starts right off with an existing git-svn clone. 323 because it starts right off with an existing git-svn clone.
225 """ 324 """
226 def __init__(self, 325 def __init__(self,
227 root_dir, project_name, remote_branch, 326 root_dir, project_name, remote_branch,
228 commit_user, commit_pwd, 327 commit_user, commit_pwd,
229 svn_repo, trunk, git_url): 328 svn_url, trunk, git_url):
230 super(GitSvnPremadeCheckout, self).__init__( 329 super(GitSvnPremadeCheckout, self).__init__(
231 root_dir, project_name, remote_branch, 330 root_dir, project_name, remote_branch,
232 commit_user, commit_pwd, 331 commit_user, commit_pwd,
233 svn_repo, trunk) 332 svn_url, trunk)
234 self.git_url = git_url 333 self.git_url = git_url
235 334
236 def prepare(self): 335 def prepare(self):
237 """Creates the initial checkout for the repo.""" 336 """Creates the initial checkout for the repo."""
238 if not os.path.isdir(self.project_path): 337 if not os.path.isdir(self.project_path):
239 logging.info('Checking out %s in %s' % 338 logging.info('Checking out %s in %s' %
240 (self.project_name, self.project_path)) 339 (self.project_name, self.project_path))
241 assert self.remote == 'origin' 340 assert self.remote == 'origin'
242 self._check_call_git( 341 self._check_call_git(
243 ['clone', self.git_url, self.project_name], 342 ['clone', self.git_url, self.project_name],
244 cwd=self.root_dir) 343 cwd=self.root_dir)
245 self._check_call_git( 344 self._check_call_git(
246 ['svn', 'init', 345 ['svn', 'init',
247 '--prefix', self.remote + '/', 346 '--prefix', self.remote + '/',
248 '-T', self.trunk, 347 '-T', self.trunk,
249 self.svn_repo]) 348 self.svn_url])
250 self._check_call_git_svn(['fetch']) 349 self._check_call_git_svn(['fetch'])
251 super(GitSvnPremadeCheckout, self).prepare() 350 super(GitSvnPremadeCheckout, self).prepare()
252 return int(self._git_svn_info('revision')) 351 return int(self._git_svn_info('revision'))
253 352
254 353
255 class GitSvnCheckout(GitSvnCheckoutBase): 354 class GitSvnCheckout(GitSvnCheckoutBase):
256 """Manages a git-svn clone. 355 """Manages a git-svn clone.
257 356
258 Using git-svn hides some of the complexity of using a svn checkout. 357 Using git-svn hides some of the complexity of using a svn checkout.
259 """ 358 """
260 def __init__(self, 359 def __init__(self,
261 root_dir, project_name, 360 root_dir, project_name,
262 commit_user, commit_pwd, 361 commit_user, commit_pwd,
263 svn_repo, trunk): 362 svn_url, trunk):
264 super(GitSvnCheckout, self).__init__( 363 super(GitSvnCheckout, self).__init__(
265 root_dir, project_name, 'trunk', 364 root_dir, project_name, 'trunk',
266 commit_user, commit_pwd, 365 commit_user, commit_pwd,
267 svn_repo, trunk) 366 svn_url, trunk)
268 367
269 def prepare(self): 368 def prepare(self):
270 """Creates the initial checkout for the repo.""" 369 """Creates the initial checkout for the repo."""
271 if not os.path.isdir(self.project_path): 370 if not os.path.isdir(self.project_path):
272 logging.info('Checking out %s in %s' % 371 logging.info('Checking out %s in %s' %
273 (self.project_name, self.project_path)) 372 (self.project_name, self.project_path))
274 # TODO: Create a shallow clone. 373 # TODO: Create a shallow clone.
275 self._check_call_git_svn( 374 self._check_call_git_svn(
276 ['clone', 375 ['clone',
277 '--prefix', self.remote + '/', 376 '--prefix', self.remote + '/',
278 '-T', self.trunk, 377 '-T', self.trunk,
279 self.svn_repo, self.project_path], 378 self.svn_url, self.project_path],
280 cwd=self.root_dir) 379 cwd=self.root_dir)
281 super(GitSvnCheckout, self).prepare() 380 super(GitSvnCheckout, self).prepare()
282 return int(self._git_svn_info('revision')) 381 return int(self._git_svn_info('revision'))
283 382
284 383
285 class ReadOnlyCheckout(object): 384 class ReadOnlyCheckout(object):
286 """Converts a checkout into a read-only one.""" 385 """Converts a checkout into a read-only one."""
287 def __init__(self, checkout): 386 def __init__(self, checkout):
288 self.checkout = checkout 387 self.checkout = checkout
289 388
(...skipping 11 matching lines...) Expand all
301 user, message)) 400 user, message))
302 return 'FAKE' 401 return 'FAKE'
303 402
304 @property 403 @property
305 def project_name(self): 404 def project_name(self):
306 return self.checkout.project_name 405 return self.checkout.project_name
307 406
308 @property 407 @property
309 def project_path(self): 408 def project_path(self):
310 return self.checkout.project_path 409 return self.checkout.project_path
OLDNEW
« no previous file with comments | « no previous file | svn_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698