OLD | NEW |
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 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. | 5 # Monkeypatch IMapIterator so that Ctrl-C can kill everything properly. |
6 # Derived from https://gist.github.com/aljungberg/626518 | 6 # Derived from https://gist.github.com/aljungberg/626518 |
7 import multiprocessing.pool | 7 import multiprocessing.pool |
8 from multiprocessing.pool import IMapIterator | 8 from multiprocessing.pool import IMapIterator |
9 def wrapper(func): | 9 def wrapper(func): |
10 def wrap(self, timeout=None): | 10 def wrap(self, timeout=None): |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 how many times the decorated |function| is called.""" | 274 how many times the decorated |function| is called.""" |
275 def _inner_gen(): | 275 def _inner_gen(): |
276 yield function() | 276 yield function() |
277 while True: | 277 while True: |
278 yield | 278 yield |
279 return _inner_gen().next | 279 return _inner_gen().next |
280 | 280 |
281 | 281 |
282 ## Git functions | 282 ## Git functions |
283 | 283 |
| 284 def die(message, *args): |
| 285 print >> sys.stderr, textwrap.dedent(message % args) |
| 286 sys.exit(1) |
| 287 |
284 | 288 |
285 def blame(filename, revision=None, porcelain=False, *_args): | 289 def blame(filename, revision=None, porcelain=False, *_args): |
286 command = ['blame'] | 290 command = ['blame'] |
287 if porcelain: | 291 if porcelain: |
288 command.append('-p') | 292 command.append('-p') |
289 if revision is not None: | 293 if revision is not None: |
290 command.append(revision) | 294 command.append(revision) |
291 command.extend(['--', filename]) | 295 command.extend(['--', filename]) |
292 return run(*command) | 296 return run(*command) |
293 | 297 |
294 | 298 |
295 def branch_config(branch, option, default=None): | 299 def branch_config(branch, option, default=None): |
296 return config('branch.%s.%s' % (branch, option), default=default) | 300 return get_config('branch.%s.%s' % (branch, option), default=default) |
297 | |
298 | |
299 def config_regexp(pattern): | |
300 if IS_WIN: # pragma: no cover | |
301 # this madness is because we call git.bat which calls git.exe which calls | |
302 # bash.exe (or something to that effect). Each layer divides the number of | |
303 # ^'s by 2. | |
304 pattern = pattern.replace('^', '^' * 8) | |
305 return run('config', '--get-regexp', pattern).splitlines() | |
306 | 301 |
307 | 302 |
308 def branch_config_map(option): | 303 def branch_config_map(option): |
309 """Return {branch: <|option| value>} for all branches.""" | 304 """Return {branch: <|option| value>} for all branches.""" |
310 try: | 305 try: |
311 reg = re.compile(r'^branch\.(.*)\.%s$' % option) | 306 reg = re.compile(r'^branch\.(.*)\.%s$' % option) |
312 lines = config_regexp(reg.pattern) | 307 lines = get_config_regexp(reg.pattern) |
313 return {reg.match(k).group(1): v for k, v in (l.split() for l in lines)} | 308 return {reg.match(k).group(1): v for k, v in (l.split() for l in lines)} |
314 except subprocess2.CalledProcessError: | 309 except subprocess2.CalledProcessError: |
315 return {} | 310 return {} |
316 | 311 |
317 | 312 |
318 def branches(*args): | 313 def branches(*args): |
319 NO_BRANCH = ('* (no branch', '* (detached', '* (HEAD detached') | 314 NO_BRANCH = ('* (no branch', '* (detached', '* (HEAD detached') |
320 | 315 |
321 key = 'depot-tools.branch-limit' | 316 key = 'depot-tools.branch-limit' |
322 limit = 20 | 317 limit = get_config_int(key, 20) |
323 try: | |
324 limit = int(config(key, limit)) | |
325 except ValueError: | |
326 pass | |
327 | 318 |
328 raw_branches = run('branch', *args).splitlines() | 319 raw_branches = run('branch', *args).splitlines() |
329 | 320 |
330 num = len(raw_branches) | 321 num = len(raw_branches) |
| 322 |
331 if num > limit: | 323 if num > limit: |
332 print >> sys.stderr, textwrap.dedent("""\ | 324 die("""\ |
333 Your git repo has too many branches (%d/%d) for this tool to work well. | 325 Your git repo has too many branches (%d/%d) for this tool to work well. |
334 | 326 |
335 You may adjust this limit by running: | 327 You may adjust this limit by running: |
336 git config %s <new_limit> | 328 git config %s <new_limit> |
337 """ % (num, limit, key)) | 329 |
338 sys.exit(1) | 330 You may also try cleaning up your old branches by running: |
| 331 git cl archive |
| 332 """, num, limit, key) |
339 | 333 |
340 for line in raw_branches: | 334 for line in raw_branches: |
341 if line.startswith(NO_BRANCH): | 335 if line.startswith(NO_BRANCH): |
342 continue | 336 continue |
343 yield line.split()[-1] | 337 yield line.split()[-1] |
344 | 338 |
345 | 339 |
346 def config(option, default=None): | 340 def get_config(option, default=None): |
347 try: | 341 try: |
348 return run('config', '--get', option) or default | 342 return run('config', '--get', option) or default |
349 except subprocess2.CalledProcessError: | 343 except subprocess2.CalledProcessError: |
350 return default | 344 return default |
351 | 345 |
352 | 346 |
353 def config_list(option): | 347 def get_config_int(option, default=0): |
| 348 assert isinstance(default, int) |
| 349 try: |
| 350 return int(get_config(option, default)) |
| 351 except ValueError: |
| 352 return default |
| 353 |
| 354 |
| 355 def get_config_list(option): |
354 try: | 356 try: |
355 return run('config', '--get-all', option).split() | 357 return run('config', '--get-all', option).split() |
356 except subprocess2.CalledProcessError: | 358 except subprocess2.CalledProcessError: |
357 return [] | 359 return [] |
358 | 360 |
359 | 361 |
| 362 def get_config_regexp(pattern): |
| 363 if IS_WIN: # pragma: no cover |
| 364 # this madness is because we call git.bat which calls git.exe which calls |
| 365 # bash.exe (or something to that effect). Each layer divides the number of |
| 366 # ^'s by 2. |
| 367 pattern = pattern.replace('^', '^' * 8) |
| 368 return run('config', '--get-regexp', pattern).splitlines() |
| 369 |
| 370 |
360 def current_branch(): | 371 def current_branch(): |
361 try: | 372 try: |
362 return run('rev-parse', '--abbrev-ref', 'HEAD') | 373 return run('rev-parse', '--abbrev-ref', 'HEAD') |
363 except subprocess2.CalledProcessError: | 374 except subprocess2.CalledProcessError: |
364 return None | 375 return None |
365 | 376 |
366 | 377 |
367 def del_branch_config(branch, option, scope='local'): | 378 def del_branch_config(branch, option, scope='local'): |
368 del_config('branch.%s.%s' % (branch, option), scope=scope) | 379 del_config('branch.%s.%s' % (branch, option), scope=scope) |
369 | 380 |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
560 del_branch_config(branch, 'base') | 571 del_branch_config(branch, 'base') |
561 del_branch_config(branch, 'base-upstream') | 572 del_branch_config(branch, 'base-upstream') |
562 | 573 |
563 | 574 |
564 def repo_root(): | 575 def repo_root(): |
565 """Returns the absolute path to the repository root.""" | 576 """Returns the absolute path to the repository root.""" |
566 return run('rev-parse', '--show-toplevel') | 577 return run('rev-parse', '--show-toplevel') |
567 | 578 |
568 | 579 |
569 def root(): | 580 def root(): |
570 return config('depot-tools.upstream', 'origin/master') | 581 return get_config('depot-tools.upstream', 'origin/master') |
571 | 582 |
572 | 583 |
573 @contextlib.contextmanager | 584 @contextlib.contextmanager |
574 def less(): # pragma: no cover | 585 def less(): # pragma: no cover |
575 """Runs 'less' as context manager yielding its stdin as a PIPE. | 586 """Runs 'less' as context manager yielding its stdin as a PIPE. |
576 | 587 |
577 Automatically checks if sys.stdout is a non-TTY stream. If so, it avoids | 588 Automatically checks if sys.stdout is a non-TTY stream. If so, it avoids |
578 running less and just yields sys.stdout. | 589 running less and just yields sys.stdout. |
579 """ | 590 """ |
580 if not setup_color.IS_TTY: | 591 if not setup_color.IS_TTY: |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
904 ['HEAD']) | 915 ['HEAD']) |
905 | 916 |
906 | 917 |
907 def clone_file(repository, new_workdir, link, operation): | 918 def clone_file(repository, new_workdir, link, operation): |
908 if not os.path.exists(os.path.join(repository, link)): | 919 if not os.path.exists(os.path.join(repository, link)): |
909 return | 920 return |
910 link_dir = os.path.dirname(os.path.join(new_workdir, link)) | 921 link_dir = os.path.dirname(os.path.join(new_workdir, link)) |
911 if not os.path.exists(link_dir): | 922 if not os.path.exists(link_dir): |
912 os.makedirs(link_dir) | 923 os.makedirs(link_dir) |
913 operation(os.path.join(repository, link), os.path.join(new_workdir, link)) | 924 operation(os.path.join(repository, link), os.path.join(new_workdir, link)) |
OLD | NEW |