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

Side by Side Diff: checkout.py

Issue 6031003: Add support for an alternate svn config directory for both svn and git-svn. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/commit-queue
Patch Set: Rebase against trunk Created 9 years, 12 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 | Annotate | Revision Log
« no previous file with comments | « .gitignore ('k') | subversion_config/config » ('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
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 except OSError: 43 except OSError:
44 return None 44 return None
45 return settings.get(key, None) 45 return settings.get(key, None)
46 46
47 47
48 class CheckoutBase(object): 48 class CheckoutBase(object):
49 def __init__(self, root_dir, project_name): 49 def __init__(self, root_dir, project_name):
50 self.root_dir = root_dir 50 self.root_dir = root_dir
51 self.project_name = project_name 51 self.project_name = project_name
52 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)
53 self.svn_config_dir = os.path.normpath(
54 os.path.join(self.root_dir, '..', 'subversion_config'))
55 assert self.root_dir
56 assert self.project_name
57 assert self.project_path
53 58
54 def get_settings(self, key): 59 def get_settings(self, key):
55 return get_code_review_setting(self.project_path, key) 60 return get_code_review_setting(self.project_path, key)
56 61
57 62
58 class SvnMixIn(object): 63 class SvnMixIn(object):
59 """MixIn class to add svn commands common to both svn and git-svn clients.""" 64 """MixIn class to add svn commands common to both svn and git-svn clients."""
60 # These members need to be set by the subclass. 65 # These members need to be set by the subclass.
61 commit_user = None 66 commit_user = None
62 commit_pwd = None 67 commit_pwd = None
63 svn_url = None 68 svn_url = None
64 project_path = None 69 project_path = None
70 svn_config_dir = None
71
72 def _add_svn_flags(self, args):
73 args = ['svn'] + args + [
74 '--config-dir', self.svn_config_dir, '--non-interactive']
75 if self.commit_user:
76 args = args + [
77 '--username', self.commit_user, '--password', self.commit_pwd]
78 return args
65 79
66 def _check_call_svn(self, args, **kwargs): 80 def _check_call_svn(self, args, **kwargs):
67 """Runs svn and throws an exception if the command failed.""" 81 """Runs svn and throws an exception if the command failed."""
68 kwargs.setdefault('cwd', self.project_path) 82 kwargs.setdefault('cwd', self.project_path)
69 return subprocess2.check_call( 83 return subprocess2.check_call(self._add_svn_flags(args), **kwargs)
70 ['svn'] + args + ['--no-auth-cache', '--non-interactive'], **kwargs)
71 84
72 def _capture_svn(self, args, **kwargs): 85 def _capture_svn(self, args, **kwargs):
73 """Runs svn and throws an exception if the command failed. 86 """Runs svn and throws an exception if the command failed.
74 87
75 Returns the output. 88 Returns the output.
76 """ 89 """
77 kwargs.setdefault('cwd', self.project_path) 90 kwargs.setdefault('cwd', self.project_path)
78 if self.commit_user: 91 return subprocess2.check_capture(self._add_svn_flags(args), **kwargs)
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 92
84 @staticmethod 93 @staticmethod
85 def _parse_svn_info(output, key): 94 def _parse_svn_info(output, key):
86 """Returns value for key from svn info output. 95 """Returns value for key from svn info output.
87 96
88 Case insensitive. 97 Case insensitive.
89 """ 98 """
90 values = {} 99 values = {}
91 for line in output.splitlines(False): 100 for line in output.splitlines(False):
92 if not line: 101 if not line:
93 continue 102 continue
94 k, v = line.split(':', 1) 103 k, v = line.split(':', 1)
95 k = k.strip().lower() 104 k = k.strip().lower()
96 v = v.strip() 105 v = v.strip()
97 assert not k in values 106 assert not k in values
98 values[k] = v 107 values[k] = v
99 return values.get(key, None) 108 return values.get(key, None)
100 109
101 def _update_committer(self, revision, new_author): 110 def _update_committer(self, revision, new_author):
102 """Changes the author of a commit a posteriori. 111 """Changes the author of a commit a posteriori.
103 112
104 This is necessary since the actual commit is done with a "commit-bot" 113 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 114 credential but the original patch author needs to be assigned authorship
106 of the revision. 115 of the revision.
107 """ 116 """
108 self._check_call_svn( 117 self._check_call_svn(
109 ['propset', '--revprop', 'svn:author', 118 ['propset', '--revprop', 'svn:author',
110 '-r', revision, 119 '-r', revision,
111 new_author, 120 new_author,
112 '--username', self.commit_user,
113 '--password', self.commit_pwd,
114 self.svn_url]) 121 self.svn_url])
115 122
116 123
117 class SvnCheckout(CheckoutBase, SvnMixIn): 124 class SvnCheckout(CheckoutBase, SvnMixIn):
118 """Manages a subversion checkout. 125 """Manages a subversion checkout.
119 126
120 Commit is not fully implemented yet. Reimplementing all the commands is 127 Commit is not fully implemented yet. Reimplementing all the commands is
121 slightly complex, svn add with history needs to be done with svn 128 slightly complex, svn add with history needs to be done with svn
122 copy/rename/move and the situation quickly becomes harder when directories are 129 copy/rename/move and the situation quickly becomes harder when directories are
123 moved. 130 moved.
124 """ 131 """
125 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url): 132 def __init__(self, root_dir, project_name, commit_user, commit_pwd, svn_url):
126 super(SvnCheckout, self).__init__(root_dir, project_name) 133 super(SvnCheckout, self).__init__(root_dir, project_name)
127 self.commit_user = commit_user 134 self.commit_user = commit_user
128 self.commit_pwd = commit_pwd 135 self.commit_pwd = commit_pwd
129 self.svn_url = svn_url 136 self.svn_url = svn_url
130 assert bool(self.commit_user) == bool(self.commit_pwd) 137 assert bool(self.commit_user) == bool(self.commit_pwd)
131 assert bool(self.svn_url) 138 assert self.svn_url
132 139
133 def prepare(self): 140 def prepare(self):
134 """Creates the initial checkouts for the repo.""" 141 """Creates the initial checkouts for the repo."""
135 # Will checkout if the directory is not present. 142 # Will checkout if the directory is not present.
136 logging.info('Checking out %s in %s' % 143 logging.info('Checking out %s in %s' %
137 (self.project_name, self.project_path)) 144 (self.project_name, self.project_path))
138 return self._revert() 145 return self._revert()
139 146
140 def apply_patch(self, patch_data): 147 def apply_patch(self, patch_data):
141 """Applies a patch.""" 148 """Applies a patch."""
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 224
218 225
219 class GitCheckoutBase(CheckoutBase): 226 class GitCheckoutBase(CheckoutBase):
220 """Base class for git checkout. Not to be used as-is.""" 227 """Base class for git checkout. Not to be used as-is."""
221 def __init__(self, root_dir, project_name, remote_branch): 228 def __init__(self, root_dir, project_name, remote_branch):
222 super(GitCheckoutBase, self).__init__(root_dir, project_name) 229 super(GitCheckoutBase, self).__init__(root_dir, project_name)
223 # There is no reason to not hardcode it. 230 # There is no reason to not hardcode it.
224 self.remote = 'origin' 231 self.remote = 'origin'
225 self.remote_branch = remote_branch 232 self.remote_branch = remote_branch
226 self.working_branch = 'working_branch' 233 self.working_branch = 'working_branch'
234 assert self.remote_branch
227 235
228 def prepare(self): 236 def prepare(self):
229 """Resets the git repository in a clean state.""" 237 """Resets the git repository in a clean state."""
230 assert os.path.isdir(self.project_path) 238 assert os.path.isdir(self.project_path)
231 self._check_call_git(['checkout', 'master', '--force']) 239 self._check_call_git(['checkout', 'master', '--force'])
232 self._check_call_git(['pull', self.remote, self.remote_branch]) 240 self._check_call_git(['pull', self.remote, self.remote_branch])
233 self._call_git(['branch', '-D', self.working_branch]) 241 self._call_git(['branch', '-D', self.working_branch])
234 242
235 def apply_patch(self, patch_data): 243 def apply_patch(self, patch_data):
236 """Applies a patch on 'working_branch'.""" 244 """Applies a patch on 'working_branch'."""
237 self._check_call_git( 245 self._check_call_git(
238 ['checkout', '-b', self.working_branch, 246 ['checkout', '-b', self.working_branch,
239 '%s/%s' % (self.remote, self.remote_branch)]) 247 '%s/%s' % (self.remote, self.remote_branch)])
240 self._check_call_git(['apply', '--index', '-p0'], stdin=patch_data) 248 self._check_call_git(['apply', '--index', '-p0'], stdin=patch_data)
241 self._check_call_git(['commit', '-m', 'Committed patch']) 249 self._check_call_git(['commit', '-m', 'Committed patch'])
242 250
243 def commit(self, commit_message, user): 251 def commit(self, commit_message, user):
252 """Updates the commit message.
253
254 Subclass needs to dcommit or push."""
244 self._check_call_git(['commit', '--amend', '-m', commit_message]) 255 self._check_call_git(['commit', '--amend', '-m', commit_message])
245 256
246 def _check_call_git(self, args, **kwargs): 257 def _check_call_git(self, args, **kwargs):
247 kwargs.setdefault('cwd', self.project_path) 258 kwargs.setdefault('cwd', self.project_path)
248 return subprocess2.check_call(['git'] + args, **kwargs) 259 return subprocess2.check_call(['git'] + args, **kwargs)
249 260
250 def _call_git(self, args, **kwargs): 261 def _call_git(self, args, **kwargs):
251 """Like check_call but doesn't throw on failure.""" 262 """Like check_call but doesn't throw on failure."""
252 kwargs.setdefault('cwd', self.project_path) 263 kwargs.setdefault('cwd', self.project_path)
253 return subprocess2.call(['git'] + args, **kwargs) 264 return subprocess2.call(['git'] + args, **kwargs)
(...skipping 10 matching lines...) Expand all
264 commit_user, commit_pwd, 275 commit_user, commit_pwd,
265 svn_url, trunk): 276 svn_url, trunk):
266 """trunk is optional.""" 277 """trunk is optional."""
267 super(GitSvnCheckoutBase, self).__init__( 278 super(GitSvnCheckoutBase, self).__init__(
268 root_dir, project_name + '.git', remote_branch) 279 root_dir, project_name + '.git', remote_branch)
269 self.commit_user = commit_user 280 self.commit_user = commit_user
270 self.commit_pwd = commit_pwd 281 self.commit_pwd = commit_pwd
271 # svn_url in this case is the root of the svn repository. 282 # svn_url in this case is the root of the svn repository.
272 self.svn_url = svn_url 283 self.svn_url = svn_url
273 self.trunk = trunk 284 self.trunk = trunk
285 assert bool(self.commit_user) == bool(self.commit_pwd)
286 assert self.svn_url
287 assert self.trunk
274 288
275 def prepare(self): 289 def prepare(self):
276 """Resets the git repository in a clean state.""" 290 """Resets the git repository in a clean state."""
277 self._check_call_git(['checkout', 'master', '--force']) 291 self._check_call_git(['checkout', 'master', '--force'])
278 self._check_call_git(['svn', 'rebase']) 292 self._check_call_git_svn(['rebase'])
279 self._call_git(['branch', '-D', self.working_branch]) 293 self._call_git(['branch', '-D', self.working_branch])
280 return int(self._git_svn_info('revision')) 294 return int(self._git_svn_info('revision'))
281 295
282 def _git_svn_info(self, key): 296 def _git_svn_info(self, key):
297 """Calls git svn info. This doesn't support nor need --config-dir."""
283 return self._parse_svn_info( 298 return self._parse_svn_info(
284 self._check_capture_git(['svn', 'info']), key) 299 self._check_capture_git(['svn', 'info']), key)
285 300
286 def commit(self, commit_message, user): 301 def commit(self, commit_message, user):
287 """Commits a patch.""" 302 """Commits a patch."""
288 logging.info('Committing patch for %s' % user) 303 logging.info('Committing patch for %s' % user)
289 # Fix the commit message. 304 # Fix the commit message.
290 super(GitSvnCheckoutBase, self).commit(commit_message, user) 305 super(GitSvnCheckoutBase, self).commit(commit_message, user)
291 # Commit with git svn dcommit, then use svn directly to update the 306 # Commit with git svn dcommit, then use svn directly to update the
292 # committer on the revision. 307 # committer on the revision.
293 self._check_call_git_svn(['dcommit', '--rmdir', '--find-copies-harder']) 308 self._check_call_git_svn(['dcommit', '--rmdir', '--find-copies-harder'])
294 revision = int(self._git_svn_info('revision')) 309 revision = int(self._git_svn_info('revision'))
295 # Fix the committer. 310 # Fix the committer.
296 self._update_committer(revision, user) 311 self._update_committer(revision, user)
297 return revision 312 return revision
298 313
299 def _cache_svn_auth(self): 314 def _cache_svn_auth(self):
300 """Caches the svn credentials. It is necessary since git-svn doesn't prompt 315 """Caches the svn credentials. It is necessary since git-svn doesn't prompt
301 for it.""" 316 for it."""
302 if not self.commit_user: 317 if not self.commit_user:
303 return 318 return
304 logging.info('Caching svn credentials for %s' % self.commit_user) 319 logging.info('Caching svn credentials for %s' % self.commit_user)
305 subprocess2.check_call( 320 self._check_call_svn(['ls', self.svn_url], cwd=None)
306 ['svn', 'ls', self.svn_url,
307 '--username', self.commit_user,
308 '--password', self.commit_pwd,
309 '--non-interactive'])
310 321
311 def _check_call_git_svn(self, args, **kwargs): 322 def _check_call_git_svn(self, args, **kwargs):
312 """Handles svn authentication while calling git svn.""" 323 """Handles svn authentication while calling git svn."""
313 args = ['svn'] + args 324 args = ['svn'] + args + ['--config-dir', self.svn_config_dir]
314 if self.commit_user: 325 self._cache_svn_auth()
315 args = args + ['--username', self.commit_user, '--no-auth-cache'] 326 return self._check_call_git(args, **kwargs)
316 return self._check_call_git(args, stdin=self.commit_pwd, **kwargs)
317 327
318 328
319 class GitSvnPremadeCheckout(GitSvnCheckoutBase): 329 class GitSvnPremadeCheckout(GitSvnCheckoutBase):
320 """Manages a git-svn clone made out from an initial git-svn seed. 330 """Manages a git-svn clone made out from an initial git-svn seed.
321 331
322 This class is very similar to GitSvnCheckout but is faster to bootstrap 332 This class is very similar to GitSvnCheckout but is faster to bootstrap
323 because it starts right off with an existing git-svn clone. 333 because it starts right off with an existing git-svn clone.
324 """ 334 """
325 def __init__(self, 335 def __init__(self,
326 root_dir, project_name, remote_branch, 336 root_dir, project_name, remote_branch,
327 commit_user, commit_pwd, 337 commit_user, commit_pwd,
328 svn_url, trunk, git_url): 338 svn_url, trunk, git_url):
329 super(GitSvnPremadeCheckout, self).__init__( 339 super(GitSvnPremadeCheckout, self).__init__(
330 root_dir, project_name, remote_branch, 340 root_dir, project_name, remote_branch,
331 commit_user, commit_pwd, 341 commit_user, commit_pwd,
332 svn_url, trunk) 342 svn_url, trunk)
333 self.git_url = git_url 343 self.git_url = git_url
344 assert self.git_url
334 345
335 def prepare(self): 346 def prepare(self):
336 """Creates the initial checkout for the repo.""" 347 """Creates the initial checkout for the repo."""
337 if not os.path.isdir(self.project_path): 348 if not os.path.isdir(self.project_path):
338 logging.info('Checking out %s in %s' % 349 logging.info('Checking out %s in %s' %
339 (self.project_name, self.project_path)) 350 (self.project_name, self.project_path))
340 assert self.remote == 'origin' 351 assert self.remote == 'origin'
341 self._check_call_git( 352 self._check_call_git(
342 ['clone', self.git_url, self.project_name], 353 ['clone', self.git_url, self.project_name],
343 cwd=self.root_dir) 354 cwd=self.root_dir)
344 self._check_call_git( 355 self._check_call_git_svn(
345 ['svn', 'init', 356 ['init',
346 '--prefix', self.remote + '/', 357 '--prefix', self.remote + '/',
347 '-T', self.trunk, 358 '-T', self.trunk,
348 self.svn_url]) 359 self.svn_url])
349 self._check_call_git_svn(['fetch']) 360 self._check_call_git_svn(['fetch'])
350 super(GitSvnPremadeCheckout, self).prepare() 361 super(GitSvnPremadeCheckout, self).prepare()
351 return int(self._git_svn_info('revision')) 362 return int(self._git_svn_info('revision'))
352 363
353 364
354 class GitSvnCheckout(GitSvnCheckoutBase): 365 class GitSvnCheckout(GitSvnCheckoutBase):
355 """Manages a git-svn clone. 366 """Manages a git-svn clone.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
400 user, message)) 411 user, message))
401 return 'FAKE' 412 return 'FAKE'
402 413
403 @property 414 @property
404 def project_name(self): 415 def project_name(self):
405 return self.checkout.project_name 416 return self.checkout.project_name
406 417
407 @property 418 @property
408 def project_path(self): 419 def project_path(self):
409 return self.checkout.project_path 420 return self.checkout.project_path
OLDNEW
« no previous file with comments | « .gitignore ('k') | subversion_config/config » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698