libv8 6.0.286.54.3 → 6.2.414.42.0beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/CHANGELOG.md +4 -0
  4. data/Rakefile +3 -3
  5. data/lib/libv8/version.rb +1 -1
  6. data/patches/0001-Build-a-standalone-static-library.patch +4 -4
  7. data/patches/0002-Don-t-compile-unnecessary-stuff.patch +12 -14
  8. data/patches/0003-Use-the-fPIC-flag-for-the-static-library.patch +4 -4
  9. data/patches/0004-Do-not-embed-debug-symbols-in-macOS-libraries.patch +4 -4
  10. data/patches/0005-avoid-constructor-inheritance-due-to-compilation-iss.patch +81 -0
  11. data/patches/9001-Arm64-Fix-host-architecture-detection.patch +37 -0
  12. data/vendor/depot_tools/.gitignore +1 -0
  13. data/vendor/depot_tools/OWNERS +0 -1
  14. data/vendor/depot_tools/auth.py +154 -6
  15. data/vendor/depot_tools/bootstrap/win/manifest.txt +1 -1
  16. data/vendor/depot_tools/bootstrap/win/manifest_bleeding_edge.txt +1 -1
  17. data/vendor/depot_tools/cipd_bin_setup.sh +12 -2
  18. data/vendor/depot_tools/cipd_manifest.txt +4 -1
  19. data/vendor/depot_tools/fetch.py +2 -0
  20. data/vendor/depot_tools/gclient.py +4 -6
  21. data/vendor/depot_tools/gclient_scm.py +12 -5
  22. data/vendor/depot_tools/gerrit_util.py +23 -1
  23. data/vendor/depot_tools/git_cache.py +59 -23
  24. data/vendor/depot_tools/git_cl.py +114 -43
  25. data/vendor/depot_tools/git_common.py +7 -0
  26. data/vendor/depot_tools/git_rebase_update.py +1 -0
  27. data/vendor/depot_tools/git_upstream_diff.py +12 -5
  28. data/vendor/depot_tools/gsutil.py +10 -0
  29. data/vendor/depot_tools/infra/config/OWNERS +0 -1
  30. data/vendor/depot_tools/infra/config/cq.cfg +6 -5
  31. data/vendor/depot_tools/infra/config/recipes.cfg +1 -1
  32. data/vendor/depot_tools/mac_toolchain +12 -0
  33. data/vendor/depot_tools/man/html/git-upstream-diff.html +10 -6
  34. data/vendor/depot_tools/man/man1/git-upstream-diff.1 +18 -7
  35. data/vendor/depot_tools/man/src/git-upstream-diff.txt +8 -5
  36. data/vendor/depot_tools/owners.py +9 -2
  37. data/vendor/depot_tools/presubmit_canned_checks.py +122 -0
  38. data/vendor/depot_tools/presubmit_support.py +57 -4
  39. data/vendor/depot_tools/recipes/OWNERS +0 -1
  40. data/vendor/depot_tools/recipes/README.recipes.md +20 -17
  41. data/vendor/depot_tools/recipes/recipe_modules/bot_update/__init__.py +1 -1
  42. data/vendor/depot_tools/recipes/recipe_modules/bot_update/api.py +15 -4
  43. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic.json +23 -0
  44. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic_with_branch_heads.json +46 -0
  45. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/buildbot.json +23 -0
  46. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/clobber.json +46 -0
  47. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/deprecated_got_revision_mapping.json +22 -0
  48. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_rebase_patch_ref.json +46 -0
  49. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_reset.json +46 -0
  50. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_shallow.json +46 -0
  51. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/reset_root_solution_revision.json +46 -0
  52. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange.json +46 -0
  53. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange_oauth2_buildbot.json +46 -0
  54. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange_oauth2_json.json +46 -0
  55. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/trychange_oauth2_json_win.json +46 -0
  56. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob.json +46 -0
  57. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_empty_revision.json +46 -0
  58. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail.json +4 -45
  59. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch.json +23 -0
  60. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch_download.json +23 -0
  61. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle.json +46 -0
  62. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle_deprecated.json +46 -0
  63. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_feature_branch.json +46 -0
  64. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_v8.json +46 -0
  65. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_v8_feature_branch.json +46 -0
  66. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_webrtc.json +46 -0
  67. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8.json +46 -0
  68. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8_head_by_default.json +46 -0
  69. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_manifest_name.json +194 -0
  70. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_manifest_name_no_patch.json +105 -0
  71. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_tags.json +46 -0
  72. data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.py +12 -2
  73. data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/bot_update.py +226 -149
  74. data/vendor/depot_tools/recipes/recipe_modules/bot_update/test_api.py +16 -5
  75. data/vendor/depot_tools/recipes/recipe_modules/gclient/config.py +1 -2
  76. data/vendor/depot_tools/recipes/recipes/fetch_end_to_end_test.expected/basic.json +11 -0
  77. data/vendor/depot_tools/rietveld.py +1 -1
  78. data/vendor/depot_tools/roll_dep.py +4 -1
  79. data/vendor/depot_tools/split_cl.py +3 -0
  80. data/vendor/depot_tools/third_party/cq_client/OWNERS +0 -1
  81. data/vendor/depot_tools/third_party/mock/LICENSE.txt +26 -0
  82. data/vendor/depot_tools/third_party/mock/README.chromium +24 -0
  83. data/vendor/depot_tools/third_party/mock/__init__.py +2366 -0
  84. data/vendor/depot_tools/watchlists.py +12 -5
  85. metadata +12 -5
  86. data/patches/0005-Fix-GCC-7-build-errors.patch +0 -147
@@ -15,4 +15,4 @@
15
15
  infra/python/cpython/windows-386 version:2.7.6
16
16
 
17
17
  @Subdir git
18
- infra/git/${platform} version:2.14.1.chromium10
18
+ infra/git/${platform} version:2.15.0.chromium12
@@ -15,4 +15,4 @@
15
15
  infra/python/cpython/${platform} version:2.7.13.chromium7
16
16
 
17
17
  @Subdir git
18
- infra/git/${platform} version:2.14.1.chromium10
18
+ infra/git/windows-${arch} version:2.15.1.2.chromium12
@@ -4,9 +4,19 @@
4
4
 
5
5
  function cipd_bin_setup {
6
6
  local MYPATH=$(dirname "${BASH_SOURCE[0]}")
7
+ local ENSURE="$MYPATH/cipd_manifest.txt"
8
+ local ROOT="$MYPATH/.cipd_bin"
9
+
10
+ UNAME=`uname -s | tr '[:upper:]' '[:lower:]'`
11
+ case $UNAME in
12
+ cygwin*)
13
+ ENSURE="$(cygpath -w $ENSURE)"
14
+ ROOT="$(cygpath -w $ROOT)"
15
+ ;;
16
+ esac
7
17
 
8
18
  "$MYPATH/cipd" ensure \
9
19
  -log-level warning \
10
- -ensure-file "$MYPATH/cipd_manifest.txt" \
11
- -root "$MYPATH/.cipd_bin"
20
+ -ensure-file "$ENSURE" \
21
+ -root "$ROOT"
12
22
  }
@@ -11,4 +11,7 @@ $VerifiedPlatform windows-386 windows-amd64
11
11
  infra/tools/luci/vpython/${platform} git_revision:a3d636052088db3daa48413b3e209eed4f5cb4ad
12
12
 
13
13
  # LUCI editor
14
- infra/tools/luci/led/${platform} git_revision:d1e9a52e1c8414cd0a0df8bec6c37f325e0fbaa1
14
+ infra/tools/luci/led/${platform} git_revision:9010ba713ba6fc9982c412161150a5cd70d5b623
15
+
16
+ # Mac toolchain installer
17
+ infra/tools/mac_toolchain/${os=mac}-${arch} git_revision:2b69be6203f56a970202d9b5984557c0453b4eb0
@@ -91,6 +91,8 @@ class GitCheckout(Checkout):
91
91
 
92
92
  def run_git(self, *cmd, **kwargs):
93
93
  print 'Running: git %s' % (' '.join(pipes.quote(x) for x in cmd))
94
+ if self.options.dry_run:
95
+ return ''
94
96
  return git_common.run(*cmd, **kwargs)
95
97
 
96
98
 
@@ -92,7 +92,6 @@ import pprint
92
92
  import re
93
93
  import sys
94
94
  import time
95
- import urllib
96
95
  import urlparse
97
96
 
98
97
  import fix_encoding
@@ -208,6 +207,8 @@ class Hook(object):
208
207
  # Python script. Run it by starting a new copy of the same
209
208
  # interpreter.
210
209
  cmd[0] = sys.executable
210
+ elif cmd[0] == 'vpython' and _detect_host_os() == 'win':
211
+ cmd[0] += '.bat'
211
212
 
212
213
  cwd = root
213
214
  if self._cwd:
@@ -1329,10 +1330,7 @@ it or fix the checkout.
1329
1330
  if cache_dir:
1330
1331
  cache_dir = os.path.join(self.root_dir, cache_dir)
1331
1332
  cache_dir = os.path.abspath(cache_dir)
1332
- # If running on a bot, force break any stale git cache locks.
1333
- if os.path.exists(cache_dir) and os.environ.get('CHROME_HEADLESS'):
1334
- subprocess2.check_call(['git', 'cache', 'unlock', '--cache-dir',
1335
- cache_dir, '--force', '--all'])
1333
+
1336
1334
  gclient_scm.GitWrapper.cache_dir = cache_dir
1337
1335
  git_cache.Mirror.SetCachePath(cache_dir)
1338
1336
 
@@ -2201,7 +2199,7 @@ def CMDconfig(parser, args):
2201
2199
  parser.add_option('--name',
2202
2200
  help='overrides the default name for the solution')
2203
2201
  parser.add_option('--deps-file', default='DEPS',
2204
- help='overrides the default name for the DEPS file for the'
2202
+ help='overrides the default name for the DEPS file for the '
2205
2203
  'main solutions and all sub-dependencies')
2206
2204
  parser.add_option('--unmanaged', action='store_true', default=False,
2207
2205
  help='overrides the default behavior to make it possible '
@@ -433,7 +433,7 @@ class GitWrapper(SCMWrapper):
433
433
  (os.path.isdir(self.checkout_path) and
434
434
  not os.path.exists(os.path.join(self.checkout_path, '.git')))):
435
435
  if mirror:
436
- self._UpdateMirror(mirror, options)
436
+ self._UpdateMirrorIfNotContains(mirror, options, rev_type, revision)
437
437
  try:
438
438
  self._Clone(revision, url, options)
439
439
  except subprocess2.CalledProcessError:
@@ -456,7 +456,7 @@ class GitWrapper(SCMWrapper):
456
456
  self._maybe_break_locks(options)
457
457
 
458
458
  if mirror:
459
- self._UpdateMirror(mirror, options)
459
+ self._UpdateMirrorIfNotContains(mirror, options, rev_type, revision)
460
460
 
461
461
  # See if the url has changed (the unittests use git://foo for the url, let
462
462
  # that through).
@@ -842,9 +842,16 @@ class GitWrapper(SCMWrapper):
842
842
  mirror_kwargs['refs'].append('refs/tags/*')
843
843
  return git_cache.Mirror(url, **mirror_kwargs)
844
844
 
845
- @staticmethod
846
- def _UpdateMirror(mirror, options):
847
- """Update a git mirror by fetching the latest commits from the remote."""
845
+ def _UpdateMirrorIfNotContains(self, mirror, options, rev_type, revision):
846
+ """Update a git mirror by fetching the latest commits from the remote,
847
+ unless mirror already contains revision whose type is sha1 hash.
848
+ """
849
+ if rev_type == 'hash' and mirror.contains_revision(revision):
850
+ if options.verbose:
851
+ self.Print('skipping mirror update, it has rev=%s already' % revision,
852
+ timestamp=False)
853
+ return
854
+
848
855
  if getattr(options, 'shallow', False):
849
856
  # HACK(hinoka): These repositories should be super shallow.
850
857
  if 'flash' in mirror.url:
@@ -665,6 +665,7 @@ def AddReviewers(host, change, reviewers=None, ccs=None, notify=True,
665
665
  path = 'changes/%s/revisions/current/review' % change
666
666
 
667
667
  body = {
668
+ 'drafts': 'KEEP',
668
669
  'reviewers': [],
669
670
  'notify': 'ALL' if notify else 'NONE',
670
671
  }
@@ -718,7 +719,7 @@ def SetReview(host, change, msg=None, labels=None, notify=None, ready=None):
718
719
  if not msg and not labels:
719
720
  return
720
721
  path = 'changes/%s/revisions/current/review' % change
721
- body = {}
722
+ body = {'drafts': 'KEEP'}
722
723
  if msg:
723
724
  body['message'] = msg
724
725
  if labels:
@@ -760,6 +761,7 @@ def ResetReviewLabels(host, change, label, value='0', message=None,
760
761
  for review in jmsg.get('labels', {}).get(label, {}).get('all', []):
761
762
  if str(review.get('value', value)) != value:
762
763
  body = {
764
+ 'drafts': 'KEEP',
763
765
  'message': message,
764
766
  'labels': {label: value},
765
767
  'on_behalf_of': review['_account_id'],
@@ -830,6 +832,26 @@ def GetAccountDetails(host, account_id='self'):
830
832
  return ReadHttpJsonResponse(conn)
831
833
 
832
834
 
835
+ def PercentEncodeForGitRef(original):
836
+ """Apply percent-encoding for strings sent to gerrit via git ref metadata.
837
+
838
+ The encoding used is based on but stricter than URL encoding (Section 2.1
839
+ of RFC 3986). The only non-escaped characters are alphanumerics, and
840
+ 'SPACE' (U+0020) can be represented as 'LOW LINE' (U+005F) or
841
+ 'PLUS SIGN' (U+002B).
842
+
843
+ For more information, see the Gerrit docs here:
844
+
845
+ https://gerrit-review.googlesource.com/Documentation/user-upload.html#message
846
+ """
847
+ safe = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '
848
+ encoded = ''.join(c if c in safe else '%%%02X' % ord(c) for c in original)
849
+
850
+ # spaces are not allowed in git refs; gerrit will interpret either '_' or
851
+ # '+' (or '%20') as space. Use '_' since that has been supported the longest.
852
+ return encoded.replace(' ', '_')
853
+
854
+
833
855
  @contextlib.contextmanager
834
856
  def tempdir():
835
857
  tdir = None
@@ -6,6 +6,7 @@
6
6
  """A git command for managing a local cache of git repositories."""
7
7
 
8
8
  from __future__ import print_function
9
+ import contextlib
9
10
  import errno
10
11
  import logging
11
12
  import optparse
@@ -25,6 +26,8 @@ import subcommand
25
26
 
26
27
  # Analogous to gc.autopacklimit git config.
27
28
  GC_AUTOPACKLIMIT = 50
29
+ if sys.platform.startswith('win'):
30
+ GC_AUTOPACKLIMIT = 9
28
31
 
29
32
  GIT_CACHE_CORRUPT_MESSAGE = 'WARNING: The Git cache is corrupt.'
30
33
 
@@ -223,6 +226,14 @@ class Mirror(object):
223
226
  def print_without_file(self, message, **_kwargs):
224
227
  self.print_func(message)
225
228
 
229
+ @contextlib.contextmanager
230
+ def print_duration_of(self, what):
231
+ start = time.time()
232
+ try:
233
+ yield
234
+ finally:
235
+ self.print('%s took %.1f minutes' % (what, (time.time() - start) / 60.0))
236
+
226
237
  @property
227
238
  def bootstrap_bucket(self):
228
239
  u = urlparse.urlparse(self.url)
@@ -324,11 +335,10 @@ class Mirror(object):
324
335
  cwd=cwd)
325
336
 
326
337
  def bootstrap_repo(self, directory):
327
- """Bootstrap the repo from Google Stroage if possible.
338
+ """Bootstrap the repo from Google Storage if possible.
328
339
 
329
340
  More apt-ly named bootstrap_repo_from_cloud_if_possible_else_do_nothing().
330
341
  """
331
-
332
342
  if not self.bootstrap_bucket:
333
343
  return False
334
344
  python_fallback = False
@@ -344,10 +354,13 @@ class Mirror(object):
344
354
  gs_folder = 'gs://%s/%s' % (self.bootstrap_bucket, self.basedir)
345
355
  gsutil = Gsutil(self.gsutil_exe, boto_path=None)
346
356
  # Get the most recent version of the zipfile.
347
- _, ls_out, _ = gsutil.check_call('ls', gs_folder)
357
+ _, ls_out, ls_err = gsutil.check_call('ls', gs_folder)
348
358
  ls_out_sorted = sorted(ls_out.splitlines())
349
359
  if not ls_out_sorted:
350
360
  # This repo is not on Google Storage.
361
+ self.print('No bootstrap file for %s found in %s, stderr:\n %s' %
362
+ (self.mirror_path, self.bootstrap_bucket,
363
+ ' '.join((ls_err or '').splitlines(True))))
351
364
  return False
352
365
  latest_checkout = ls_out_sorted[-1]
353
366
 
@@ -355,28 +368,30 @@ class Mirror(object):
355
368
  try:
356
369
  tempdir = tempfile.mkdtemp(prefix='_cache_tmp', dir=self.GetCachePath())
357
370
  self.print('Downloading %s' % latest_checkout)
358
- code = gsutil.call('cp', latest_checkout, tempdir)
371
+ with self.print_duration_of('download'):
372
+ code = gsutil.call('cp', latest_checkout, tempdir)
359
373
  if code:
360
374
  return False
361
375
  filename = os.path.join(tempdir, latest_checkout.split('/')[-1])
362
376
 
363
377
  # Unpack the file with 7z on Windows, unzip on linux, or fallback.
364
- if not python_fallback:
365
- if sys.platform.startswith('win'):
366
- cmd = ['7z', 'x', '-o%s' % directory, '-tzip', filename]
378
+ with self.print_duration_of('unzip'):
379
+ if not python_fallback:
380
+ if sys.platform.startswith('win'):
381
+ cmd = ['7z', 'x', '-o%s' % directory, '-tzip', filename]
382
+ else:
383
+ cmd = ['unzip', filename, '-d', directory]
384
+ retcode = subprocess.call(cmd)
367
385
  else:
368
- cmd = ['unzip', filename, '-d', directory]
369
- retcode = subprocess.call(cmd)
370
- else:
371
- try:
372
- with zipfile.ZipFile(filename, 'r') as f:
373
- f.printdir()
374
- f.extractall(directory)
375
- except Exception as e:
376
- self.print('Encountered error: %s' % str(e), file=sys.stderr)
377
- retcode = 1
378
- else:
379
- retcode = 0
386
+ try:
387
+ with zipfile.ZipFile(filename, 'r') as f:
388
+ f.printdir()
389
+ f.extractall(directory)
390
+ except Exception as e:
391
+ self.print('Encountered error: %s' % str(e), file=sys.stderr)
392
+ retcode = 1
393
+ else:
394
+ retcode = 0
380
395
  finally:
381
396
  # Clean up the downloaded zipfile.
382
397
  #
@@ -396,6 +411,24 @@ class Mirror(object):
396
411
  return False
397
412
  return True
398
413
 
414
+ def contains_revision(self, revision):
415
+ if not self.exists():
416
+ return False
417
+
418
+ if sys.platform.startswith('win'):
419
+ # Windows .bat scripts use ^ as escape sequence, which means we have to
420
+ # escape it with itself for every .bat invocation.
421
+ needle = '%s^^^^{commit}' % revision
422
+ else:
423
+ needle = '%s^{commit}' % revision
424
+ try:
425
+ # cat-file exits with 0 on success, that is git object of given hash was
426
+ # found.
427
+ self.RunGit(['cat-file', '-e', needle])
428
+ return True
429
+ except subprocess.CalledProcessError:
430
+ return False
431
+
399
432
  def exists(self):
400
433
  return os.path.isfile(os.path.join(self.mirror_path, 'config'))
401
434
 
@@ -432,6 +465,8 @@ class Mirror(object):
432
465
 
433
466
  if os.path.isdir(pack_dir):
434
467
  pack_files = [f for f in os.listdir(pack_dir) if f.endswith('.pack')]
468
+ self.print('%s has %d .pack files, re-bootstrapping if >%d' %
469
+ (self.mirror_path, len(pack_files), GC_AUTOPACKLIMIT))
435
470
 
436
471
  should_bootstrap = (force or
437
472
  not self.exists() or
@@ -455,8 +490,8 @@ class Mirror(object):
455
490
  else:
456
491
  # Bootstrap failed, previous cache exists; warn and continue.
457
492
  logging.warn(
458
- 'Git cache has a lot of pack files (%d). Tried to re-bootstrap '
459
- 'but failed. Continuing with non-optimized repository.'
493
+ 'Git cache has a lot of pack files (%d). Tried to re-bootstrap '
494
+ 'but failed. Continuing with non-optimized repository.'
460
495
  % len(pack_files))
461
496
  gclient_utils.rmtree(tempdir)
462
497
  tempdir = None
@@ -481,7 +516,8 @@ class Mirror(object):
481
516
  for spec in fetch_specs:
482
517
  try:
483
518
  self.print('Fetching %s' % spec)
484
- self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True)
519
+ with self.print_duration_of('fetch %s' % spec):
520
+ self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True)
485
521
  except subprocess.CalledProcessError:
486
522
  if spec == '+refs/heads/*:refs/heads/*':
487
523
  raise ClobberNeeded() # Corrupted cache.
@@ -509,7 +545,7 @@ class Mirror(object):
509
545
  self.print(GIT_CACHE_CORRUPT_MESSAGE)
510
546
  tempdir = self._ensure_bootstrapped(depth, bootstrap, force=True)
511
547
  assert tempdir
512
- self._fetch(tempdir or self.mirror_path, verbose, depth)
548
+ self._fetch(tempdir, verbose, depth)
513
549
  finally:
514
550
  if tempdir:
515
551
  if os.path.exists(self.mirror_path):
@@ -27,6 +27,7 @@ import re
27
27
  import shutil
28
28
  import stat
29
29
  import sys
30
+ import tempfile
30
31
  import textwrap
31
32
  import urllib
32
33
  import urllib2
@@ -1154,7 +1155,7 @@ class Changelist(object):
1154
1155
  self.lookedup_patchset = False
1155
1156
  self.patchset = None
1156
1157
  self.cc = None
1157
- self.watchers = ()
1158
+ self.more_cc = []
1158
1159
  self._remote = None
1159
1160
 
1160
1161
  self._codereview_impl = None
@@ -1202,21 +1203,19 @@ class Changelist(object):
1202
1203
  """
1203
1204
  if self.cc is None:
1204
1205
  base_cc = settings.GetDefaultCCList()
1205
- more_cc = ','.join(self.watchers)
1206
+ more_cc = ','.join(self.more_cc)
1206
1207
  self.cc = ','.join(filter(None, (base_cc, more_cc))) or ''
1207
1208
  return self.cc
1208
1209
 
1209
1210
  def GetCCListWithoutDefault(self):
1210
1211
  """Return the users cc'd on this CL excluding default ones."""
1211
1212
  if self.cc is None:
1212
- self.cc = ','.join(self.watchers)
1213
+ self.cc = ','.join(self.more_cc)
1213
1214
  return self.cc
1214
1215
 
1215
- def SetWatchers(self, watchers):
1216
- """Sets the list of email addresses that should be cc'd based on the changed
1217
- files in this CL.
1218
- """
1219
- self.watchers = watchers
1216
+ def ExtendCC(self, more_cc):
1217
+ """Extends the list of users to cc on this CL based on the changed files."""
1218
+ self.more_cc.extend(more_cc)
1220
1219
 
1221
1220
  def GetBranch(self):
1222
1221
  """Returns the short branch name, e.g. 'master'."""
@@ -1626,7 +1625,7 @@ class Changelist(object):
1626
1625
  watchlist = watchlists.Watchlists(change.RepositoryRoot())
1627
1626
  files = [f.LocalPath() for f in change.AffectedFiles()]
1628
1627
  if not options.bypass_watchlists:
1629
- self.SetWatchers(watchlist.GetWatchersForPaths(files))
1628
+ self.ExtendCC(watchlist.GetWatchersForPaths(files))
1630
1629
 
1631
1630
  if not options.bypass_hooks:
1632
1631
  if options.reviewers or options.tbrs or options.add_owners_to:
@@ -1645,6 +1644,7 @@ class Changelist(object):
1645
1644
  return 1
1646
1645
  if not options.reviewers and hook_results.reviewers:
1647
1646
  options.reviewers = hook_results.reviewers.split(',')
1647
+ self.ExtendCC(hook_results.more_cc)
1648
1648
 
1649
1649
  # TODO(tandrii): Checking local patchset against remote patchset is only
1650
1650
  # supported for Rietveld. Extend it to Gerrit or remove it completely.
@@ -2900,7 +2900,12 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
2900
2900
 
2901
2901
  # This may be None; default fallback value is determined in logic below.
2902
2902
  title = options.title
2903
- automatic_title = False
2903
+
2904
+ # Extract bug number from branch name.
2905
+ bug = options.bug
2906
+ match = re.match(r'(?:bug|fix)[_-]?(\d+)', self.GetBranch())
2907
+ if not bug and match:
2908
+ bug = match.group(1)
2904
2909
 
2905
2910
  if options.squash:
2906
2911
  self._GerritCommitMsgHookCheck(offer_removal=not options.force)
@@ -2924,8 +2929,6 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
2924
2929
  else:
2925
2930
  title = ask_for_data(
2926
2931
  'Title for patchset [%s]: ' % default_title) or default_title
2927
- if title == default_title:
2928
- automatic_title = True
2929
2932
  change_id = self._GetChangeDetail()['change_id']
2930
2933
  while True:
2931
2934
  footer_change_ids = git_footers.get_footer_change_id(message)
@@ -2952,7 +2955,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
2952
2955
  confirm_or_exit(action='edit')
2953
2956
  if not options.force:
2954
2957
  change_desc = ChangeDescription(message)
2955
- change_desc.prompt(bug=options.bug)
2958
+ change_desc.prompt(bug=bug)
2956
2959
  message = change_desc.description
2957
2960
  if not message:
2958
2961
  DieWithError("Description is empty. Aborting...")
@@ -2971,11 +2974,10 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
2971
2974
  change_desc = ChangeDescription(message)
2972
2975
 
2973
2976
  if not options.force:
2974
- change_desc.prompt(bug=options.bug)
2977
+ change_desc.prompt(bug=bug)
2975
2978
  # On first upload, patchset title is always this string, while
2976
2979
  # --title flag gets converted to first line of message.
2977
2980
  title = 'Initial upload'
2978
- automatic_title = True
2979
2981
  if not change_desc.description:
2980
2982
  DieWithError("Description is empty. Aborting...")
2981
2983
  change_ids = git_footers.get_footer_change_id(change_desc.description)
@@ -2998,8 +3000,12 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
2998
3000
  parent = self._ComputeParent(remote, upstream_branch, custom_cl_base,
2999
3001
  options.force, change_desc)
3000
3002
  tree = RunGit(['rev-parse', 'HEAD:']).strip()
3001
- ref_to_push = RunGit(['commit-tree', tree, '-p', parent,
3002
- '-m', change_desc.description]).strip()
3003
+ with tempfile.NamedTemporaryFile(delete=False) as desc_tempfile:
3004
+ desc_tempfile.write(change_desc.description)
3005
+ desc_tempfile.close()
3006
+ ref_to_push = RunGit(['commit-tree', tree, '-p', parent,
3007
+ '-F', desc_tempfile.name]).strip()
3008
+ os.remove(desc_tempfile.name)
3003
3009
  else:
3004
3010
  change_desc = ChangeDescription(
3005
3011
  options.message or CreateDescriptionFromLog(git_diff_args))
@@ -3045,26 +3051,17 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
3045
3051
  if options.send_mail:
3046
3052
  refspec_opts.append('ready')
3047
3053
  refspec_opts.append('notify=ALL')
3054
+ elif not self.GetIssue():
3055
+ refspec_opts.append('wip')
3048
3056
  else:
3049
- if not self.GetIssue():
3050
- refspec_opts.append('wip')
3051
- else:
3052
- refspec_opts.append('notify=NONE')
3057
+ refspec_opts.append('notify=NONE')
3053
3058
 
3054
3059
  # TODO(tandrii): options.message should be posted as a comment
3055
3060
  # if --send-mail is set on non-initial upload as Rietveld used to do it.
3056
3061
 
3057
3062
  if title:
3058
- if not re.match(r'^[\w ]+$', title):
3059
- title = re.sub(r'[^\w ]', '', title)
3060
- if not automatic_title:
3061
- print('WARNING: Patchset title may only contain alphanumeric chars '
3062
- 'and spaces. You can edit it in the UI. '
3063
- 'See https://crbug.com/663787.\n'
3064
- 'Cleaned up title: %s' % title)
3065
- # Per doc, spaces must be converted to underscores, and Gerrit will do the
3066
- # reverse on its side.
3067
- refspec_opts.append('m=' + title.replace(' ', '_'))
3063
+ # Punctuation and whitespace in |title| must be percent-encoded.
3064
+ refspec_opts.append('m=' + gerrit_util.PercentEncodeForGitRef(title))
3068
3065
 
3069
3066
  if options.private:
3070
3067
  refspec_opts.append('private')
@@ -3074,6 +3071,12 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
3074
3071
  # https://gerrit-review.googlesource.com/Documentation/user-upload.html#topic
3075
3072
  refspec_opts.append('topic=%s' % options.topic)
3076
3073
 
3074
+ # Gerrit sorts hashtags, so order is not important.
3075
+ hashtags = {change_desc.sanitize_hash_tag(t) for t in options.hashtags}
3076
+ if not self.GetIssue():
3077
+ hashtags.update(change_desc.get_hash_tags())
3078
+ refspec_opts += ['hashtag=%s' % t for t in sorted(hashtags)]
3079
+
3077
3080
  refspec_suffix = ''
3078
3081
  if refspec_opts:
3079
3082
  refspec_suffix = '%' + ','.join(refspec_opts)
@@ -3345,6 +3348,10 @@ class ChangeDescription(object):
3345
3348
  CC_LINE = r'^[ \t]*(CC)[ \t]*=[ \t]*(.*?)[ \t]*$'
3346
3349
  BUG_LINE = r'^[ \t]*(?:(BUG)[ \t]*=|Bug:)[ \t]*(.*?)[ \t]*$'
3347
3350
  CHERRY_PICK_LINE = r'^\(cherry picked from commit [a-fA-F0-9]{40}\)$'
3351
+ STRIP_HASH_TAG_PREFIX = r'^(\s*(revert|reland)( "|:)?\s*)*'
3352
+ BRACKET_HASH_TAG = r'\s*\[([^\[\]]+)\]'
3353
+ COLON_SEPARATED_HASH_TAG = r'^([a-zA-Z0-9_\- ]+):'
3354
+ BAD_HASH_TAG_CHUNK = r'[^a-zA-Z0-9]+'
3348
3355
 
3349
3356
  def __init__(self, description):
3350
3357
  self._description_lines = (description or '').strip().splitlines()
@@ -3517,6 +3524,37 @@ class ChangeDescription(object):
3517
3524
  cced = [match.group(2).strip() for match in matches if match]
3518
3525
  return cleanup_list(cced)
3519
3526
 
3527
+ def get_hash_tags(self):
3528
+ """Extracts and sanitizes a list of Gerrit hashtags."""
3529
+ subject = (self._description_lines or ('',))[0]
3530
+ subject = re.sub(
3531
+ self.STRIP_HASH_TAG_PREFIX, '', subject, flags=re.IGNORECASE)
3532
+
3533
+ tags = []
3534
+ start = 0
3535
+ bracket_exp = re.compile(self.BRACKET_HASH_TAG)
3536
+ while True:
3537
+ m = bracket_exp.match(subject, start)
3538
+ if not m:
3539
+ break
3540
+ tags.append(self.sanitize_hash_tag(m.group(1)))
3541
+ start = m.end()
3542
+
3543
+ if not tags:
3544
+ # Try "Tag: " prefix.
3545
+ m = re.match(self.COLON_SEPARATED_HASH_TAG, subject)
3546
+ if m:
3547
+ tags.append(self.sanitize_hash_tag(m.group(1)))
3548
+ return tags
3549
+
3550
+ @classmethod
3551
+ def sanitize_hash_tag(cls, tag):
3552
+ """Returns a sanitized Gerrit hash tag.
3553
+
3554
+ A sanitized hashtag can be used as a git push refspec parameter value.
3555
+ """
3556
+ return re.sub(cls.BAD_HASH_TAG_CHUNK, '-', tag).strip('-').lower()
3557
+
3520
3558
  def update_with_git_number_footers(self, parent_hash, parent_msg, dest_ref):
3521
3559
  """Updates this commit description given the parent.
3522
3560
 
@@ -4495,7 +4533,7 @@ def CMDissue(parser, args):
4495
4533
 
4496
4534
  if options.reverse:
4497
4535
  branches = RunGit(['for-each-ref', 'refs/heads',
4498
- '--format=%(refname:short)']).splitlines()
4536
+ '--format=%(refname)']).splitlines()
4499
4537
  # Reverse issue lookup.
4500
4538
  issue_branch_map = {}
4501
4539
  for branch in branches:
@@ -4743,6 +4781,8 @@ def CMDpresubmit(parser, args):
4743
4781
  help='Run upload hook instead of the push hook')
4744
4782
  parser.add_option('-f', '--force', action='store_true',
4745
4783
  help='Run checks even if tree is dirty')
4784
+ parser.add_option('--all', action='store_true',
4785
+ help='Run checks against all files, not just modified ones')
4746
4786
  auth.add_auth_options(parser)
4747
4787
  options, args = parser.parse_args(args)
4748
4788
  auth_config = auth.extract_auth_config_from_options(options)
@@ -4758,11 +4798,26 @@ def CMDpresubmit(parser, args):
4758
4798
  # Default to diffing against the common ancestor of the upstream branch.
4759
4799
  base_branch = cl.GetCommonAncestorWithUpstream()
4760
4800
 
4801
+ if options.all:
4802
+ base_change = cl.GetChange(base_branch, None)
4803
+ files = [('M', f) for f in base_change.AllFiles()]
4804
+ change = presubmit_support.GitChange(
4805
+ base_change.Name(),
4806
+ base_change.FullDescriptionText(),
4807
+ base_change.RepositoryRoot(),
4808
+ files,
4809
+ base_change.issue,
4810
+ base_change.patchset,
4811
+ base_change.author_email,
4812
+ base_change._upstream)
4813
+ else:
4814
+ change = cl.GetChange(base_branch, None)
4815
+
4761
4816
  cl.RunHook(
4762
4817
  committing=not options.upload,
4763
4818
  may_prompt=False,
4764
4819
  verbose=options.verbose,
4765
- change=cl.GetChange(base_branch, None))
4820
+ change=change)
4766
4821
  return 0
4767
4822
 
4768
4823
 
@@ -4859,7 +4914,7 @@ def cleanup_list(l):
4859
4914
  return sorted(filter(None, stripped_items))
4860
4915
 
4861
4916
 
4862
- @subcommand.usage('[args to "git diff"]')
4917
+ @subcommand.usage('[flags]')
4863
4918
  def CMDupload(parser, args):
4864
4919
  """Uploads the current changelist to codereview.
4865
4920
 
@@ -4868,6 +4923,16 @@ def CMDupload(parser, args):
4868
4923
  To unset run:
4869
4924
  git config --unset branch.branch_name.skip-deps-uploads
4870
4925
  Can also set the above globally by using the --global flag.
4926
+
4927
+ If the name of the checked out branch starts with "bug-" or "fix-" followed by
4928
+ a bug number, this bug number is automatically populated in the CL
4929
+ description.
4930
+
4931
+ If subject contains text in square brackets or has "<text>: " prefix, such
4932
+ text(s) is treated as Gerrit hashtags. For example, CLs with subjects
4933
+ [git-cl] add support for hashtags
4934
+ Foo bar: implement foo
4935
+ will be hashtagged with "git-cl" and "foo-bar" respectively.
4871
4936
  """
4872
4937
  parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
4873
4938
  help='bypass upload presubmit hook')
@@ -4894,6 +4959,10 @@ def CMDupload(parser, args):
4894
4959
  parser.add_option('--cc',
4895
4960
  action='append', default=[],
4896
4961
  help='cc email addresses')
4962
+ parser.add_option('--hashtag', dest='hashtags',
4963
+ action='append', default=[],
4964
+ help=('Gerrit hashtag for new CL; '
4965
+ 'can be applied multiple times'))
4897
4966
  parser.add_option('-s', '--send-mail', action='store_true',
4898
4967
  help='send email to reviewer(s) and cc(s) immediately')
4899
4968
  parser.add_option('--emulate_svn_auto_props',
@@ -4904,22 +4973,17 @@ def CMDupload(parser, args):
4904
4973
  parser.add_option('-c', '--use-commit-queue', action='store_true',
4905
4974
  help='tell the commit queue to commit this patchset; '
4906
4975
  'implies --send-mail')
4907
- parser.add_option('--private', action='store_true',
4908
- help='set the review private (rietveld only)')
4909
4976
  parser.add_option('--target_branch',
4910
4977
  '--target-branch',
4911
4978
  metavar='TARGET',
4912
4979
  help='Apply CL to remote ref TARGET. ' +
4913
4980
  'Default: remote branch head, or master')
4914
4981
  parser.add_option('--squash', action='store_true',
4915
- help='Squash multiple commits into one (Gerrit only)')
4982
+ help='Squash multiple commits into one')
4916
4983
  parser.add_option('--no-squash', action='store_true',
4917
- help='Don\'t squash multiple commits into one ' +
4918
- '(Gerrit only)')
4984
+ help='Don\'t squash multiple commits into one')
4919
4985
  parser.add_option('--topic', default=None,
4920
- help='Topic to specify when uploading (Gerrit only)')
4921
- parser.add_option('--email', default=None,
4922
- help='email address to use to connect to Rietveld')
4986
+ help='Topic to specify when uploading')
4923
4987
  parser.add_option('--tbr-owners', dest='add_owners_to', action='store_const',
4924
4988
  const='TBR', help='add a set of OWNERS to TBR')
4925
4989
  parser.add_option('--r-owners', dest='add_owners_to', action='store_const',
@@ -4932,6 +4996,12 @@ def CMDupload(parser, args):
4932
4996
  help='Uploads CLs of all the local branches that depend on '
4933
4997
  'the current branch')
4934
4998
 
4999
+ # TODO: remove Rietveld flags
5000
+ parser.add_option('--private', action='store_true',
5001
+ help='set the review private (rietveld only)')
5002
+ parser.add_option('--email', default=None,
5003
+ help='email address to use to connect to Rietveld')
5004
+
4935
5005
  orig_args = args
4936
5006
  add_git_similarity(parser)
4937
5007
  auth.add_auth_options(parser)
@@ -4976,7 +5046,8 @@ def CMDsplit(parser, args):
4976
5046
  the shared OWNERS file.
4977
5047
  """
4978
5048
  parser.add_option("-d", "--description", dest="description_file",
4979
- help="A text file containing a CL description. ")
5049
+ help="A text file containing a CL description in which "
5050
+ "$directory will be replaced by each CL's directory.")
4980
5051
  parser.add_option("-c", "--comment", dest="comment_file",
4981
5052
  help="A text file containing a CL comment.")
4982
5053
  options, _ = parser.parse_args(args)