libv8 6.7.288.46.1 → 7.3.492.27.0beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/CHANGELOG.md +4 -0
- data/README.md +2 -0
- data/ext/libv8/builder.rb +6 -2
- data/lib/libv8/version.rb +1 -1
- data/vendor/depot_tools/.gitattributes +1 -0
- data/vendor/depot_tools/.gitignore +7 -0
- data/vendor/depot_tools/CROS_OWNERS +5 -0
- data/vendor/depot_tools/OWNERS +12 -1
- data/vendor/depot_tools/PRESUBMIT.py +16 -9
- data/vendor/depot_tools/README.md +9 -2
- data/vendor/depot_tools/autoninja +14 -6
- data/vendor/depot_tools/autoninja.bat +11 -1
- data/vendor/depot_tools/autoninja.py +40 -18
- data/vendor/depot_tools/bb +12 -0
- data/vendor/depot_tools/bb.bat +7 -0
- data/vendor/depot_tools/bootstrap/win/manifest.txt +1 -1
- data/vendor/depot_tools/bootstrap/win/manifest_bleeding_edge.txt +1 -1
- data/vendor/depot_tools/bootstrap/win/win_tools.py +2 -1
- data/vendor/depot_tools/buildbucket.py +57 -4
- data/vendor/depot_tools/cipd +157 -44
- data/vendor/depot_tools/cipd.bat +51 -14
- data/vendor/depot_tools/cipd.ps1 +104 -42
- data/vendor/depot_tools/cipd_client_version +1 -1
- data/vendor/depot_tools/cipd_client_version.digests +21 -0
- data/vendor/depot_tools/cipd_manifest.txt +19 -6
- data/vendor/depot_tools/cipd_manifest.versions +318 -0
- data/vendor/depot_tools/clang_format.py +4 -4
- data/vendor/depot_tools/cpplint.py +44 -199
- data/vendor/depot_tools/dart_format.py +2 -2
- data/vendor/depot_tools/detect_host_arch.py +8 -3
- data/vendor/depot_tools/download_from_google_storage.py +47 -39
- data/vendor/depot_tools/fetch.py +30 -18
- data/vendor/depot_tools/fetch_configs/android_internal.py +34 -0
- data/vendor/depot_tools/fetch_configs/chromium.py +18 -1
- data/vendor/depot_tools/fetch_configs/config_util.py +4 -2
- data/vendor/depot_tools/fetch_configs/inspector_protocol.py +40 -0
- data/vendor/depot_tools/fetch_configs/node-ci.py +41 -0
- data/vendor/depot_tools/fix_encoding.py +3 -3
- data/vendor/depot_tools/gclient +1 -1
- data/vendor/depot_tools/gclient.py +415 -198
- data/vendor/depot_tools/gclient_eval.py +220 -171
- data/vendor/depot_tools/gclient_paths.py +142 -0
- data/vendor/depot_tools/gclient_scm.py +200 -51
- data/vendor/depot_tools/gclient_utils.py +88 -191
- data/vendor/depot_tools/gerrit_client.py +13 -0
- data/vendor/depot_tools/gerrit_util.py +158 -23
- data/vendor/depot_tools/git-nav-upstream +1 -1
- data/vendor/depot_tools/git_cache.py +77 -24
- data/vendor/depot_tools/git_cl.py +705 -1099
- data/vendor/depot_tools/git_common.py +9 -6
- data/vendor/depot_tools/git_map_branches.py +19 -2
- data/vendor/depot_tools/git_nav_downstream.py +3 -4
- data/vendor/depot_tools/git_rebase_update.py +14 -0
- data/vendor/depot_tools/git_reparent_branch.py +8 -2
- data/vendor/depot_tools/gn.py +38 -3
- data/vendor/depot_tools/gsutil.py +8 -3
- data/vendor/depot_tools/gsutil.py.bat +15 -0
- data/vendor/depot_tools/gsutil.vpython +16 -0
- data/vendor/depot_tools/infra/config/OWNERS +0 -1
- data/vendor/depot_tools/infra/config/recipes.cfg +3 -2
- data/vendor/depot_tools/lucicfg +12 -0
- data/vendor/depot_tools/lucicfg.bat +7 -0
- data/vendor/depot_tools/man/html/git-map-branches.html +34 -2
- data/vendor/depot_tools/man/html/git-new-branch.html +40 -32
- data/vendor/depot_tools/man/man1/git-map-branches.1 +24 -5
- data/vendor/depot_tools/man/man1/git-new-branch.1 +35 -27
- data/vendor/depot_tools/man/src/git-map-branches.demo.1.sh +1 -0
- data/vendor/depot_tools/man/src/git-map-branches.txt +10 -0
- data/vendor/depot_tools/man/src/git-new-branch.demo.1.sh +9 -4
- data/vendor/depot_tools/man/src/git-new-branch.txt +1 -1
- data/vendor/depot_tools/metrics.README.md +98 -0
- data/vendor/depot_tools/metrics.py +296 -0
- data/vendor/depot_tools/metrics_utils.py +303 -0
- data/vendor/depot_tools/my_activity.py +91 -29
- data/vendor/depot_tools/ninja +1 -1
- data/vendor/depot_tools/ninjalog.README.md +64 -0
- data/vendor/depot_tools/ninjalog_uploader.py +232 -0
- data/vendor/depot_tools/ninjalog_uploader_wrapper.py +116 -0
- data/vendor/depot_tools/owners.py +30 -13
- data/vendor/depot_tools/owners_finder.py +5 -2
- data/vendor/depot_tools/presubmit_canned_checks.py +188 -29
- data/vendor/depot_tools/presubmit_support.py +18 -41
- data/vendor/depot_tools/pylintrc +23 -19
- data/vendor/depot_tools/recipes/OWNERS +2 -0
- data/vendor/depot_tools/recipes/README.recipes.md +344 -151
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/OWNERS +2 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/__init__.py +2 -16
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/api.py +141 -99
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic.json +5 -8
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic_luci.json +5 -8
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/basic_with_branch_heads.json +6 -98
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/clobber.json +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/deprecated_got_revision_mapping.json +45 -5
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_rebase_patch_ref.json +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/gerrit_no_reset.json +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{tryjob.json → input_commit_with_id_without_repo.json} +6 -11
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{tryjob_empty_revision.json → multiple_patch_refs.json} +8 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/no_apply_patch_on_gclient.json +19 -29
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/{trychange.json → refs.json} +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/reset_root_solution_revision.json +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail.json +51 -6
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch.json +50 -6
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_fail_patch_download.json +51 -6
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle.json +17 -25
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_branch_heads.json +17 -25
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_feature_branch.json +18 -26
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_v8_feature_branch.json +18 -26
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_webrtc.json +26 -28
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8.json +45 -5
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_v8_head_by_default.json +17 -25
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/unrecognized_commit_repo.json +13 -0
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_manifest_name.json +13 -152
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_tags.json +4 -9
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.py +185 -202
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/bot_update.py +52 -157
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/test_api.py +5 -14
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/tests/ensure_checkout.py +34 -0
- data/vendor/depot_tools/recipes/recipe_modules/cipd/api.py +14 -2
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/basic.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/basic_pkg.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/describe-failed.json +7 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/describe-many-instances.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/mac64.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_file.json +9 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_mode.json +9 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/pkg_bad_verfile.json +9 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/full.expected/win64.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/junk arch.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/junk bits.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_arm_32.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_arm_64.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_intel_32.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_intel_64.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/linux_mips_64.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/mac_intel_64.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/win_intel_32.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/cipd/examples/platform_suffix.expected/win_intel_64.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/api.py +13 -8
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/basic.json +18 -12
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/basic_luci.json +18 -12
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.expected/win.json +18 -12
- data/vendor/depot_tools/recipes/recipe_modules/depot_tools/examples/full.py +3 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/__init__.py +1 -0
- data/vendor/depot_tools/recipes/recipe_modules/gclient/api.py +58 -46
- data/vendor/depot_tools/recipes/recipe_modules/gclient/config.py +65 -22
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/basic.json +20 -21
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/buildbot.json +20 -21
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/revision.json +20 -21
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json +20 -21
- data/vendor/depot_tools/recipes/recipe_modules/gclient/examples/full.py +5 -2
- data/vendor/depot_tools/recipes/recipe_modules/gclient/tests/patch_project.py +62 -14
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/api.py +24 -38
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.expected/basic.json +56 -50
- data/vendor/depot_tools/recipes/recipe_modules/gerrit/examples/full.py +15 -9
- data/vendor/depot_tools/recipes/recipe_modules/git/__init__.py +4 -1
- data/vendor/depot_tools/recipes/recipe_modules/git/api.py +34 -22
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_branch.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_file_name.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_hash.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_luci.json +222 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_ref.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_submodule_update_force.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/basic_tags.json +224 -0
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/can_fail_build.json +10 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/cannot_fail_build.json +5 -7
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/cat-file_test.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_delta.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_failed.json +5 -7
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_with_bad_output.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/count-objects_with_bad_output_fails_build.json +10 -5
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/curl_trace_file.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/git-cache-checkout.json +8 -9
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/platform_win.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/rebase_failed.json +12 -8
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/remote_not_origin.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.expected/set_got_revision.json +5 -6
- data/vendor/depot_tools/recipes/recipe_modules/git/examples/full.py +27 -11
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/api.py +1 -1
- data/vendor/depot_tools/recipes/recipe_modules/git_cl/examples/full.expected/basic.json +12 -13
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/__init__.py +5 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/api.py +120 -5
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/examples/full.expected/basic.json +45 -3
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/examples/full.py +25 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/resources/gerrit_client.py +56 -4
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/basic.json +6 -0
- data/vendor/depot_tools/recipes/recipe_modules/gitiles/tests/parse_repo_url.py +49 -0
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/api.py +24 -13
- data/vendor/depot_tools/recipes/recipe_modules/gsutil/examples/full.expected/basic.json +13 -14
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/basic.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_buildbot_linux.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_buildbot_mac.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_buildbot_win.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_generic_linux.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_generic_mac.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_generic_win.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_kitchen_linux.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_kitchen_mac.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/examples/full.expected/paths_kitchen_win.json +2 -3
- data/vendor/depot_tools/recipes/recipe_modules/infra_paths/path_config.py +1 -2
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/__init__.py +35 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/api.py +116 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/linux.json +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/mac.json +82 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.expected/win.json +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/osx_sdk/examples/full.py +23 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/__init__.py +1 -0
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/api.py +2 -7
- data/vendor/depot_tools/recipes/recipe_modules/presubmit/examples/full.expected/basic.json +7 -6
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/__init__.py +1 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/api.py +117 -8
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/basic_tags.json +4 -5
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/set_failure_hash_with_no_steps.json +7 -4
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_gerrit_patch.json +98 -7
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_gerrit_patch_and_target_ref.json +147 -0
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_git_patch.json +8 -5
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_git_patch_luci.json +8 -5
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_wrong_patch.json +9 -6
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.expected/with_wrong_patch_new.json +9 -6
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/examples/full.py +27 -2
- data/vendor/depot_tools/recipes/recipe_modules/tryserver/test_api.py +14 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/__init__.py +25 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/api.py +137 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/linux.json +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/mac.json +22 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.expected/win.json +107 -0
- data/vendor/depot_tools/recipes/recipe_modules/windows_sdk/examples/full.py +26 -0
- data/vendor/depot_tools/recipes/recipes.py +37 -27
- data/vendor/depot_tools/recipes/recipes/fetch_end_to_end_test.expected/basic.json +7 -10
- data/vendor/depot_tools/repo +34 -8
- data/vendor/depot_tools/roll_dep.py +52 -49
- data/vendor/depot_tools/scm.py +38 -23
- data/vendor/depot_tools/setup_color.py +4 -2
- data/vendor/depot_tools/split_cl.py +32 -4
- data/vendor/depot_tools/subprocess2.py +22 -4
- data/vendor/depot_tools/third_party/httplib2/README.chromium +2 -2
- data/vendor/depot_tools/third_party/httplib2/__init__.py +242 -158
- data/vendor/depot_tools/third_party/httplib2/cacerts.txt +57 -44
- data/vendor/depot_tools/third_party/httplib2/socks.py +15 -5
- data/vendor/depot_tools/third_party/logilab/README.chromium +2 -4
- data/vendor/depot_tools/third_party/logilab/astroid/README.chromium +2 -1
- data/vendor/depot_tools/third_party/logilab/astroid/__init__.py +10 -5
- data/vendor/depot_tools/third_party/logilab/astroid/__pkginfo__.py +5 -5
- data/vendor/depot_tools/third_party/logilab/astroid/arguments.py +233 -0
- data/vendor/depot_tools/third_party/logilab/astroid/as_string.py +82 -33
- data/vendor/depot_tools/third_party/logilab/astroid/bases.py +137 -153
- data/vendor/depot_tools/third_party/logilab/astroid/brain/{builtin_inference.py → brain_builtin_inference.py} +117 -26
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_dateutil.py +15 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/{py2gi.py → brain_gi.py} +48 -8
- data/vendor/depot_tools/third_party/logilab/astroid/brain/{py2mechanize.py → brain_mechanize.py} +0 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/{pynose.py → brain_nose.py} +4 -1
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_numpy.py +62 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_pytest.py +76 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_qt.py +44 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/{pysix_moves.py → brain_six.py} +28 -1
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_ssl.py +65 -0
- data/vendor/depot_tools/third_party/logilab/astroid/brain/brain_stdlib.py +473 -0
- data/vendor/depot_tools/third_party/logilab/astroid/builder.py +104 -81
- data/vendor/depot_tools/third_party/logilab/astroid/context.py +81 -0
- data/vendor/depot_tools/third_party/logilab/astroid/decorators.py +75 -0
- data/vendor/depot_tools/third_party/logilab/astroid/exceptions.py +20 -0
- data/vendor/depot_tools/third_party/logilab/astroid/inference.py +137 -183
- data/vendor/depot_tools/third_party/logilab/astroid/manager.py +45 -169
- data/vendor/depot_tools/third_party/logilab/astroid/mixins.py +37 -14
- data/vendor/depot_tools/third_party/logilab/astroid/modutils.py +112 -41
- data/vendor/depot_tools/third_party/logilab/astroid/node_classes.py +243 -156
- data/vendor/depot_tools/third_party/logilab/astroid/nodes.py +35 -22
- data/vendor/depot_tools/third_party/logilab/astroid/objects.py +186 -0
- data/vendor/depot_tools/third_party/logilab/astroid/protocols.py +157 -102
- data/vendor/depot_tools/third_party/logilab/astroid/raw_building.py +32 -8
- data/vendor/depot_tools/third_party/logilab/astroid/rebuilder.py +372 -309
- data/vendor/depot_tools/third_party/logilab/astroid/scoped_nodes.py +652 -420
- data/vendor/depot_tools/third_party/logilab/astroid/test_utils.py +4 -21
- data/vendor/depot_tools/third_party/logilab/astroid/transforms.py +96 -0
- data/vendor/depot_tools/third_party/logilab/astroid/util.py +89 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/LICENSE +19 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/README.chromium +11 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/__init__.py +20 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/cext.c +1421 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/compat.py +9 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/simple.py +246 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/slots.py +414 -0
- data/vendor/depot_tools/third_party/logilab/lazy_object_proxy/utils.py +13 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/LICENSE +24 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/README.chromium +11 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/__init__.py +19 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/_wrappers.c +2729 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/arguments.py +96 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/decorators.py +512 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/importer.py +228 -0
- data/vendor/depot_tools/third_party/logilab/wrapt/wrappers.py +901 -0
- data/vendor/depot_tools/third_party/pylint/README.chromium +2 -25
- data/vendor/depot_tools/third_party/pylint/__pkginfo__.py +13 -3
- data/vendor/depot_tools/third_party/pylint/checkers/__init__.py +1 -2
- data/vendor/depot_tools/third_party/pylint/checkers/async.py +82 -0
- data/vendor/depot_tools/third_party/pylint/checkers/base.py +893 -119
- data/vendor/depot_tools/third_party/pylint/checkers/classes.py +342 -204
- data/vendor/depot_tools/third_party/pylint/checkers/design_analysis.py +51 -34
- data/vendor/depot_tools/third_party/pylint/checkers/exceptions.py +84 -47
- data/vendor/depot_tools/third_party/pylint/checkers/format.py +55 -30
- data/vendor/depot_tools/third_party/pylint/checkers/imports.py +314 -73
- data/vendor/depot_tools/third_party/pylint/checkers/logging.py +10 -8
- data/vendor/depot_tools/third_party/pylint/checkers/misc.py +2 -1
- data/vendor/depot_tools/third_party/pylint/checkers/newstyle.py +45 -48
- data/vendor/depot_tools/third_party/pylint/checkers/python3.py +31 -21
- data/vendor/depot_tools/third_party/pylint/checkers/raw_metrics.py +3 -3
- data/vendor/depot_tools/third_party/pylint/checkers/similar.py +4 -5
- data/vendor/depot_tools/third_party/pylint/checkers/spelling.py +24 -10
- data/vendor/depot_tools/third_party/pylint/checkers/stdlib.py +120 -56
- data/vendor/depot_tools/third_party/pylint/checkers/strings.py +38 -35
- data/vendor/depot_tools/third_party/pylint/checkers/typecheck.py +485 -138
- data/vendor/depot_tools/third_party/pylint/checkers/utils.py +319 -142
- data/vendor/depot_tools/third_party/pylint/checkers/variables.py +329 -207
- data/vendor/depot_tools/third_party/pylint/config.py +739 -76
- data/vendor/depot_tools/third_party/pylint/epylint.py +9 -5
- data/vendor/depot_tools/third_party/pylint/extensions/__init__.py +0 -0
- data/vendor/depot_tools/third_party/pylint/extensions/check_docs.py +311 -0
- data/vendor/depot_tools/third_party/pylint/extensions/check_elif.py +62 -0
- data/vendor/depot_tools/third_party/{logilab/common → pylint}/graph.py +30 -133
- data/vendor/depot_tools/third_party/pylint/gui.py +2 -2
- data/vendor/depot_tools/third_party/pylint/interfaces.py +21 -3
- data/vendor/depot_tools/third_party/pylint/lint.py +123 -140
- data/vendor/depot_tools/third_party/pylint/pyreverse/diadefslib.py +10 -13
- data/vendor/depot_tools/third_party/pylint/pyreverse/diagrams.py +15 -4
- data/vendor/depot_tools/third_party/pylint/pyreverse/inspector.py +372 -0
- data/vendor/depot_tools/third_party/pylint/pyreverse/main.py +30 -7
- data/vendor/depot_tools/third_party/pylint/pyreverse/utils.py +80 -2
- data/vendor/depot_tools/third_party/{logilab/common → pylint/pyreverse}/vcgutils.py +19 -37
- data/vendor/depot_tools/third_party/pylint/pyreverse/writer.py +3 -4
- data/vendor/depot_tools/third_party/pylint/reporters/__init__.py +34 -18
- data/vendor/depot_tools/third_party/pylint/reporters/guireporter.py +1 -1
- data/vendor/depot_tools/third_party/pylint/reporters/html.py +10 -3
- data/vendor/depot_tools/third_party/pylint/reporters/json.py +10 -4
- data/vendor/depot_tools/third_party/pylint/reporters/text.py +94 -3
- data/vendor/depot_tools/third_party/pylint/reporters/ureports/__init__.py +106 -0
- data/vendor/depot_tools/third_party/{logilab/common → pylint/reporters}/ureports/html_writer.py +17 -57
- data/vendor/depot_tools/third_party/{logilab/common → pylint/reporters}/ureports/nodes.py +52 -74
- data/vendor/depot_tools/third_party/{logilab/common → pylint/reporters}/ureports/text_writer.py +14 -60
- data/vendor/depot_tools/third_party/pylint/testutils.py +22 -20
- data/vendor/depot_tools/third_party/pylint/utils.py +268 -44
- data/vendor/depot_tools/third_party/repo/progress.py +42 -0
- data/vendor/depot_tools/update_depot_tools +1 -1
- data/vendor/depot_tools/upload_metrics.py +25 -0
- data/vendor/depot_tools/win_toolchain/get_toolchain_if_necessary.py +45 -15
- data/vendor/depot_tools/win_toolchain/package_from_installed.py +71 -24
- data/vendor/depot_tools/yapf +1 -1
- data/vendor/depot_tools/yapf.bat +1 -1
- metadata +92 -77
- data/vendor/depot_tools/git-crsync +0 -3
- data/vendor/depot_tools/infra/config/cq.cfg +0 -32
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/apply_gerrit_ref.json +0 -29
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/apply_gerrit_ref_custom.json +0 -29
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/buildbot.json +0 -105
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/shallow.json +0 -195
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_angle_deprecated.json +0 -248
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/tryjob_gerrit_v8.json +0 -248
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/examples/full.expected/with_manifest_name_no_patch.json +0 -105
- data/vendor/depot_tools/recipes/recipe_modules/bot_update/resources/apply_gerrit.py +0 -33
- data/vendor/depot_tools/third_party/logilab/astroid/brain/py2pytest.py +0 -31
- data/vendor/depot_tools/third_party/logilab/astroid/brain/py2qt4.py +0 -22
- data/vendor/depot_tools/third_party/logilab/astroid/brain/py2stdlib.py +0 -334
- data/vendor/depot_tools/third_party/logilab/astroid/inspector.py +0 -273
- data/vendor/depot_tools/third_party/logilab/astroid/utils.py +0 -239
- data/vendor/depot_tools/third_party/logilab/common/LICENSE.txt +0 -339
- data/vendor/depot_tools/third_party/logilab/common/README.chromium +0 -11
- data/vendor/depot_tools/third_party/logilab/common/__init__.py +0 -175
- data/vendor/depot_tools/third_party/logilab/common/__pkginfo__.py +0 -57
- data/vendor/depot_tools/third_party/logilab/common/cache.py +0 -114
- data/vendor/depot_tools/third_party/logilab/common/changelog.py +0 -238
- data/vendor/depot_tools/third_party/logilab/common/clcommands.py +0 -334
- data/vendor/depot_tools/third_party/logilab/common/cli.py +0 -211
- data/vendor/depot_tools/third_party/logilab/common/compat.py +0 -78
- data/vendor/depot_tools/third_party/logilab/common/configuration.py +0 -1105
- data/vendor/depot_tools/third_party/logilab/common/contexts.py +0 -5
- data/vendor/depot_tools/third_party/logilab/common/corbautils.py +0 -117
- data/vendor/depot_tools/third_party/logilab/common/daemon.py +0 -101
- data/vendor/depot_tools/third_party/logilab/common/date.py +0 -335
- data/vendor/depot_tools/third_party/logilab/common/dbf.py +0 -231
- data/vendor/depot_tools/third_party/logilab/common/debugger.py +0 -214
- data/vendor/depot_tools/third_party/logilab/common/decorators.py +0 -281
- data/vendor/depot_tools/third_party/logilab/common/deprecation.py +0 -189
- data/vendor/depot_tools/third_party/logilab/common/fileutils.py +0 -404
- data/vendor/depot_tools/third_party/logilab/common/interface.py +0 -71
- data/vendor/depot_tools/third_party/logilab/common/logging_ext.py +0 -195
- data/vendor/depot_tools/third_party/logilab/common/modutils.py +0 -702
- data/vendor/depot_tools/third_party/logilab/common/optik_ext.py +0 -392
- data/vendor/depot_tools/third_party/logilab/common/optparser.py +0 -92
- data/vendor/depot_tools/third_party/logilab/common/proc.py +0 -277
- data/vendor/depot_tools/third_party/logilab/common/pyro_ext.py +0 -180
- data/vendor/depot_tools/third_party/logilab/common/pytest.py +0 -1199
- data/vendor/depot_tools/third_party/logilab/common/registry.py +0 -1119
- data/vendor/depot_tools/third_party/logilab/common/shellutils.py +0 -462
- data/vendor/depot_tools/third_party/logilab/common/sphinx_ext.py +0 -87
- data/vendor/depot_tools/third_party/logilab/common/sphinxutils.py +0 -122
- data/vendor/depot_tools/third_party/logilab/common/table.py +0 -929
- data/vendor/depot_tools/third_party/logilab/common/tasksqueue.py +0 -101
- data/vendor/depot_tools/third_party/logilab/common/testlib.py +0 -1392
- data/vendor/depot_tools/third_party/logilab/common/textutils.py +0 -537
- data/vendor/depot_tools/third_party/logilab/common/tree.py +0 -369
- data/vendor/depot_tools/third_party/logilab/common/umessage.py +0 -194
- data/vendor/depot_tools/third_party/logilab/common/ureports/__init__.py +0 -172
- data/vendor/depot_tools/third_party/logilab/common/ureports/docbook_writer.py +0 -140
- data/vendor/depot_tools/third_party/logilab/common/urllib2ext.py +0 -89
- data/vendor/depot_tools/third_party/logilab/common/visitor.py +0 -109
- data/vendor/depot_tools/third_party/logilab/common/xmlrpcutils.py +0 -131
- data/vendor/depot_tools/third_party/logilab/common/xmlutils.py +0 -61
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
# Copyright (C) 2008 Evan Martin <martine@danga.com>
|
7
7
|
|
8
|
-
"""A git-command for integrating reviews on
|
8
|
+
"""A git-command for integrating reviews on Gerrit."""
|
9
9
|
|
10
10
|
from __future__ import print_function
|
11
11
|
|
@@ -29,6 +29,7 @@ import stat
|
|
29
29
|
import sys
|
30
30
|
import tempfile
|
31
31
|
import textwrap
|
32
|
+
import time
|
32
33
|
import urllib
|
33
34
|
import urllib2
|
34
35
|
import urlparse
|
@@ -43,7 +44,6 @@ except ImportError:
|
|
43
44
|
|
44
45
|
from third_party import colorama
|
45
46
|
from third_party import httplib2
|
46
|
-
from third_party import upload
|
47
47
|
import auth
|
48
48
|
import checkout
|
49
49
|
import clang_format
|
@@ -55,10 +55,11 @@ import gerrit_util
|
|
55
55
|
import git_cache
|
56
56
|
import git_common
|
57
57
|
import git_footers
|
58
|
+
import metrics
|
59
|
+
import metrics_utils
|
58
60
|
import owners
|
59
61
|
import owners_finder
|
60
62
|
import presubmit_support
|
61
|
-
import rietveld
|
62
63
|
import scm
|
63
64
|
import split_cl
|
64
65
|
import subcommand
|
@@ -68,7 +69,6 @@ import watchlists
|
|
68
69
|
__version__ = '2.0'
|
69
70
|
|
70
71
|
COMMIT_BOT_EMAIL = 'commit-bot@chromium.org'
|
71
|
-
DEFAULT_SERVER = 'https://codereview.chromium.org'
|
72
72
|
POSTUPSTREAM_HOOK = '.git/hooks/post-cl-land'
|
73
73
|
DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup'
|
74
74
|
REFS_THAT_ALIAS_TO_OTHER_REFS = {
|
@@ -80,6 +80,9 @@ REFS_THAT_ALIAS_TO_OTHER_REFS = {
|
|
80
80
|
DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
|
81
81
|
DEFAULT_LINT_IGNORE_REGEX = r"$^"
|
82
82
|
|
83
|
+
# File name for yapf style config files.
|
84
|
+
YAPF_CONFIG_FILENAME = '.style.yapf'
|
85
|
+
|
83
86
|
# Buildbucket master name prefix.
|
84
87
|
MASTER_PREFIX = 'master.'
|
85
88
|
|
@@ -106,8 +109,7 @@ def DieWithError(message, change_desc=None):
|
|
106
109
|
|
107
110
|
def SaveDescriptionBackup(change_desc):
|
108
111
|
backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE)
|
109
|
-
print('\
|
110
|
-
backup_path)
|
112
|
+
print('\nsaving CL description to %s\n' % backup_path)
|
111
113
|
backup_file = open(backup_path, 'w')
|
112
114
|
backup_file.write(change_desc.description)
|
113
115
|
backup_file.close()
|
@@ -176,10 +178,15 @@ def BranchExists(branch):
|
|
176
178
|
def time_sleep(seconds):
|
177
179
|
# Use this so that it can be mocked in tests without interfering with python
|
178
180
|
# system machinery.
|
179
|
-
import time # Local import to discourage others from importing time globally.
|
180
181
|
return time.sleep(seconds)
|
181
182
|
|
182
183
|
|
184
|
+
def time_time():
|
185
|
+
# Use this so that it can be mocked in tests without interfering with python
|
186
|
+
# system machinery.
|
187
|
+
return time.time()
|
188
|
+
|
189
|
+
|
183
190
|
def ask_for_data(prompt):
|
184
191
|
try:
|
185
192
|
return raw_input(prompt)
|
@@ -452,8 +459,7 @@ def _trigger_try_jobs(auth_config, changelist, buckets, options, patchset):
|
|
452
459
|
buildbucket_put_url = (
|
453
460
|
'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format(
|
454
461
|
hostname=options.buildbucket_host))
|
455
|
-
buildset = 'patch/
|
456
|
-
codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
|
462
|
+
buildset = 'patch/gerrit/{hostname}/{issue}/{patch}'.format(
|
457
463
|
hostname=codereview_host,
|
458
464
|
issue=changelist.GetIssue(),
|
459
465
|
patch=patchset)
|
@@ -545,8 +551,7 @@ def fetch_try_jobs(auth_config, changelist, buildbucket_host,
|
|
545
551
|
|
546
552
|
http.force_exception_to_status_code = True
|
547
553
|
|
548
|
-
buildset = 'patch/
|
549
|
-
codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
|
554
|
+
buildset = 'patch/gerrit/{hostname}/{issue}/{patch}'.format(
|
550
555
|
hostname=codereview_host,
|
551
556
|
issue=changelist.GetIssue(),
|
552
557
|
patch=patchset)
|
@@ -666,6 +671,88 @@ def print_try_jobs(options, builds):
|
|
666
671
|
print('Total: %d try jobs' % total)
|
667
672
|
|
668
673
|
|
674
|
+
def _ComputeDiffLineRanges(files, upstream_commit):
|
675
|
+
"""Gets the changed line ranges for each file since upstream_commit.
|
676
|
+
|
677
|
+
Parses a git diff on provided files and returns a dict that maps a file name
|
678
|
+
to an ordered list of range tuples in the form (start_line, count).
|
679
|
+
Ranges are in the same format as a git diff.
|
680
|
+
"""
|
681
|
+
# If files is empty then diff_output will be a full diff.
|
682
|
+
if len(files) == 0:
|
683
|
+
return {}
|
684
|
+
|
685
|
+
# Take the git diff and find the line ranges where there are changes.
|
686
|
+
diff_cmd = BuildGitDiffCmd('-U0', upstream_commit, files, allow_prefix=True)
|
687
|
+
diff_output = RunGit(diff_cmd)
|
688
|
+
|
689
|
+
pattern = r'(?:^diff --git a/(?:.*) b/(.*))|(?:^@@.*\+(.*) @@)'
|
690
|
+
# 2 capture groups
|
691
|
+
# 0 == fname of diff file
|
692
|
+
# 1 == 'diff_start,diff_count' or 'diff_start'
|
693
|
+
# will match each of
|
694
|
+
# diff --git a/foo.foo b/foo.py
|
695
|
+
# @@ -12,2 +14,3 @@
|
696
|
+
# @@ -12,2 +17 @@
|
697
|
+
# running re.findall on the above string with pattern will give
|
698
|
+
# [('foo.py', ''), ('', '14,3'), ('', '17')]
|
699
|
+
|
700
|
+
curr_file = None
|
701
|
+
line_diffs = {}
|
702
|
+
for match in re.findall(pattern, diff_output, flags=re.MULTILINE):
|
703
|
+
if match[0] != '':
|
704
|
+
# Will match the second filename in diff --git a/a.py b/b.py.
|
705
|
+
curr_file = match[0]
|
706
|
+
line_diffs[curr_file] = []
|
707
|
+
else:
|
708
|
+
# Matches +14,3
|
709
|
+
if ',' in match[1]:
|
710
|
+
diff_start, diff_count = match[1].split(',')
|
711
|
+
else:
|
712
|
+
# Single line changes are of the form +12 instead of +12,1.
|
713
|
+
diff_start = match[1]
|
714
|
+
diff_count = 1
|
715
|
+
|
716
|
+
diff_start = int(diff_start)
|
717
|
+
diff_count = int(diff_count)
|
718
|
+
|
719
|
+
# If diff_count == 0 this is a removal we can ignore.
|
720
|
+
line_diffs[curr_file].append((diff_start, diff_count))
|
721
|
+
|
722
|
+
return line_diffs
|
723
|
+
|
724
|
+
|
725
|
+
def _FindYapfConfigFile(fpath, yapf_config_cache, top_dir=None):
|
726
|
+
"""Checks if a yapf file is in any parent directory of fpath until top_dir.
|
727
|
+
|
728
|
+
Recursively checks parent directories to find yapf file and if no yapf file
|
729
|
+
is found returns None. Uses yapf_config_cache as a cache for
|
730
|
+
previously found configs.
|
731
|
+
"""
|
732
|
+
fpath = os.path.abspath(fpath)
|
733
|
+
# Return result if we've already computed it.
|
734
|
+
if fpath in yapf_config_cache:
|
735
|
+
return yapf_config_cache[fpath]
|
736
|
+
|
737
|
+
parent_dir = os.path.dirname(fpath)
|
738
|
+
if os.path.isfile(fpath):
|
739
|
+
ret = _FindYapfConfigFile(parent_dir, yapf_config_cache, top_dir)
|
740
|
+
else:
|
741
|
+
# Otherwise fpath is a directory
|
742
|
+
yapf_file = os.path.join(fpath, YAPF_CONFIG_FILENAME)
|
743
|
+
if os.path.isfile(yapf_file):
|
744
|
+
ret = yapf_file
|
745
|
+
elif fpath == top_dir or parent_dir == fpath:
|
746
|
+
# If we're at the top level directory, or if we're at root
|
747
|
+
# there is no provided style.
|
748
|
+
ret = None
|
749
|
+
else:
|
750
|
+
# Otherwise recurse on the current directory.
|
751
|
+
ret = _FindYapfConfigFile(parent_dir, yapf_config_cache, top_dir)
|
752
|
+
yapf_config_cache[fpath] = ret
|
753
|
+
return ret
|
754
|
+
|
755
|
+
|
669
756
|
def write_try_results_json(output_file, builds):
|
670
757
|
"""Writes a subset of the data from fetch_try_jobs to a file as JSON.
|
671
758
|
|
@@ -691,7 +778,7 @@ def write_try_results_json(output_file, builds):
|
|
691
778
|
|
692
779
|
converted = []
|
693
780
|
for _, build in sorted(builds.items()):
|
694
|
-
|
781
|
+
converted.append(convert_build_dict(build))
|
695
782
|
write_json(output_file, converted)
|
696
783
|
|
697
784
|
|
@@ -719,7 +806,6 @@ class BuildbucketResponseException(Exception):
|
|
719
806
|
|
720
807
|
class Settings(object):
|
721
808
|
def __init__(self):
|
722
|
-
self.default_server = None
|
723
809
|
self.cc = None
|
724
810
|
self.root = None
|
725
811
|
self.tree_status_url = None
|
@@ -729,8 +815,6 @@ class Settings(object):
|
|
729
815
|
self.squash_gerrit_uploads = None
|
730
816
|
self.gerrit_skip_ensure_authenticated = None
|
731
817
|
self.git_editor = None
|
732
|
-
self.project = None
|
733
|
-
self.force_https_commit_url = None
|
734
818
|
|
735
819
|
def LazyUpdateIfNeeded(self):
|
736
820
|
"""Updates the settings from a codereview.settings file, if available."""
|
@@ -746,20 +830,6 @@ class Settings(object):
|
|
746
830
|
LoadCodereviewSettingsFromFile(cr_settings_file)
|
747
831
|
self.updated = True
|
748
832
|
|
749
|
-
def GetDefaultServerUrl(self, error_ok=False):
|
750
|
-
if not self.default_server:
|
751
|
-
self.LazyUpdateIfNeeded()
|
752
|
-
self.default_server = gclient_utils.UpgradeToHttps(
|
753
|
-
self._GetRietveldConfig('server', error_ok=True))
|
754
|
-
if error_ok:
|
755
|
-
return self.default_server
|
756
|
-
if not self.default_server:
|
757
|
-
error_message = ('Could not find settings file. You must configure '
|
758
|
-
'your review setup by running "git cl config".')
|
759
|
-
self.default_server = gclient_utils.UpgradeToHttps(
|
760
|
-
self._GetRietveldConfig('server', error_message=error_message))
|
761
|
-
return self.default_server
|
762
|
-
|
763
833
|
@staticmethod
|
764
834
|
def GetRelativeRoot():
|
765
835
|
return RunGit(['rev-parse', '--show-cdup']).strip()
|
@@ -769,50 +839,30 @@ class Settings(object):
|
|
769
839
|
self.root = os.path.abspath(self.GetRelativeRoot())
|
770
840
|
return self.root
|
771
841
|
|
772
|
-
def GetGitMirror(self, remote='origin'):
|
773
|
-
"""If this checkout is from a local git mirror, return a Mirror object."""
|
774
|
-
local_url = RunGit(['config', '--get', 'remote.%s.url' % remote]).strip()
|
775
|
-
if not os.path.isdir(local_url):
|
776
|
-
return None
|
777
|
-
git_cache.Mirror.SetCachePath(os.path.dirname(local_url))
|
778
|
-
remote_url = git_cache.Mirror.CacheDirToUrl(local_url)
|
779
|
-
# Use the /dev/null print_func to avoid terminal spew.
|
780
|
-
mirror = git_cache.Mirror(remote_url, print_func=lambda *args: None)
|
781
|
-
if mirror.exists():
|
782
|
-
return mirror
|
783
|
-
return None
|
784
|
-
|
785
842
|
def GetTreeStatusUrl(self, error_ok=False):
|
786
843
|
if not self.tree_status_url:
|
787
844
|
error_message = ('You must configure your tree status URL by running '
|
788
845
|
'"git cl config".')
|
789
|
-
self.tree_status_url = self.
|
790
|
-
'tree-status-url', error_ok=error_ok,
|
846
|
+
self.tree_status_url = self._GetConfig(
|
847
|
+
'rietveld.tree-status-url', error_ok=error_ok,
|
848
|
+
error_message=error_message)
|
791
849
|
return self.tree_status_url
|
792
850
|
|
793
851
|
def GetViewVCUrl(self):
|
794
852
|
if not self.viewvc_url:
|
795
|
-
self.viewvc_url = self.
|
853
|
+
self.viewvc_url = self._GetConfig('rietveld.viewvc-url', error_ok=True)
|
796
854
|
return self.viewvc_url
|
797
855
|
|
798
856
|
def GetBugPrefix(self):
|
799
|
-
return self.
|
800
|
-
|
801
|
-
def GetIsSkipDependencyUpload(self, branch_name):
|
802
|
-
"""Returns true if specified branch should skip dep uploads."""
|
803
|
-
return self._GetBranchConfig(branch_name, 'skip-deps-uploads',
|
804
|
-
error_ok=True)
|
857
|
+
return self._GetConfig('rietveld.bug-prefix', error_ok=True)
|
805
858
|
|
806
859
|
def GetRunPostUploadHook(self):
|
807
|
-
run_post_upload_hook = self.
|
808
|
-
'run-post-upload-hook', error_ok=True)
|
860
|
+
run_post_upload_hook = self._GetConfig(
|
861
|
+
'rietveld.run-post-upload-hook', error_ok=True)
|
809
862
|
return run_post_upload_hook == "True"
|
810
863
|
|
811
864
|
def GetDefaultCCList(self):
|
812
|
-
return self.
|
813
|
-
|
814
|
-
def GetDefaultPrivateFlag(self):
|
815
|
-
return self._GetRietveldConfig('private', error_ok=True)
|
865
|
+
return self._GetConfig('rietveld.cc', error_ok=True)
|
816
866
|
|
817
867
|
def GetIsGerrit(self):
|
818
868
|
"""Return true if this repo is associated with gerrit code review system."""
|
@@ -858,28 +908,21 @@ class Settings(object):
|
|
858
908
|
def GetGitEditor(self):
|
859
909
|
"""Return the editor specified in the git config, or None if none is."""
|
860
910
|
if self.git_editor is None:
|
861
|
-
|
911
|
+
# Git requires single quotes for paths with spaces. We need to replace
|
912
|
+
# them with double quotes for Windows to treat such paths as a single
|
913
|
+
# path.
|
914
|
+
self.git_editor = self._GetConfig(
|
915
|
+
'core.editor', error_ok=True).replace('\'', '"')
|
862
916
|
return self.git_editor or None
|
863
917
|
|
864
918
|
def GetLintRegex(self):
|
865
|
-
return (self.
|
919
|
+
return (self._GetConfig('rietveld.cpplint-regex', error_ok=True) or
|
866
920
|
DEFAULT_LINT_REGEX)
|
867
921
|
|
868
922
|
def GetLintIgnoreRegex(self):
|
869
|
-
return (self.
|
923
|
+
return (self._GetConfig('rietveld.cpplint-ignore-regex', error_ok=True) or
|
870
924
|
DEFAULT_LINT_IGNORE_REGEX)
|
871
925
|
|
872
|
-
def GetProject(self):
|
873
|
-
if not self.project:
|
874
|
-
self.project = self._GetRietveldConfig('project', error_ok=True)
|
875
|
-
return self.project
|
876
|
-
|
877
|
-
def _GetRietveldConfig(self, param, **kwargs):
|
878
|
-
return self._GetConfig('rietveld.' + param, **kwargs)
|
879
|
-
|
880
|
-
def _GetBranchConfig(self, branch_name, param, **kwargs):
|
881
|
-
return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs)
|
882
|
-
|
883
926
|
def _GetConfig(self, param, **kwargs):
|
884
927
|
self.LazyUpdateIfNeeded()
|
885
928
|
return RunGit(['config', param], **kwargs).strip()
|
@@ -915,57 +958,6 @@ def _get_gerrit_project_config_file(remote_url):
|
|
915
958
|
yield project_config_file
|
916
959
|
|
917
960
|
|
918
|
-
def _is_git_numberer_enabled(remote_url, remote_ref):
|
919
|
-
"""Returns True if Git Numberer is enabled on this ref."""
|
920
|
-
# TODO(tandrii): this should be deleted once repos below are 100% on Gerrit.
|
921
|
-
KNOWN_PROJECTS_WHITELIST = [
|
922
|
-
'chromium/src',
|
923
|
-
'external/webrtc',
|
924
|
-
'v8/v8',
|
925
|
-
'infra/experimental',
|
926
|
-
# For webrtc.googlesource.com/src.
|
927
|
-
'src',
|
928
|
-
]
|
929
|
-
|
930
|
-
assert remote_ref and remote_ref.startswith('refs/'), remote_ref
|
931
|
-
url_parts = urlparse.urlparse(remote_url)
|
932
|
-
project_name = url_parts.path.lstrip('/').rstrip('git./')
|
933
|
-
for known in KNOWN_PROJECTS_WHITELIST:
|
934
|
-
if project_name.endswith(known):
|
935
|
-
break
|
936
|
-
else:
|
937
|
-
# Early exit to avoid extra fetches for repos that aren't using Git
|
938
|
-
# Numberer.
|
939
|
-
return False
|
940
|
-
|
941
|
-
with _get_gerrit_project_config_file(remote_url) as project_config_file:
|
942
|
-
if project_config_file is None:
|
943
|
-
# Failed to fetch project.config, which shouldn't happen on open source
|
944
|
-
# repos KNOWN_PROJECTS_WHITELIST.
|
945
|
-
return False
|
946
|
-
def get_opts(x):
|
947
|
-
code, out = RunGitWithCode(
|
948
|
-
['config', '-f', project_config_file, '--get-all',
|
949
|
-
'plugin.git-numberer.validate-%s-refglob' % x])
|
950
|
-
if code == 0:
|
951
|
-
return out.strip().splitlines()
|
952
|
-
return []
|
953
|
-
enabled, disabled = map(get_opts, ['enabled', 'disabled'])
|
954
|
-
|
955
|
-
logging.info('validator config enabled %s disabled %s refglobs for '
|
956
|
-
'(this ref: %s)', enabled, disabled, remote_ref)
|
957
|
-
|
958
|
-
def match_refglobs(refglobs):
|
959
|
-
for refglob in refglobs:
|
960
|
-
if remote_ref == refglob or fnmatch.fnmatch(remote_ref, refglob):
|
961
|
-
return True
|
962
|
-
return False
|
963
|
-
|
964
|
-
if match_refglobs(disabled):
|
965
|
-
return False
|
966
|
-
return match_refglobs(enabled)
|
967
|
-
|
968
|
-
|
969
961
|
def ShortBranchName(branch):
|
970
962
|
"""Convert a name like 'refs/heads/foo' to just 'foo'."""
|
971
963
|
return branch.replace('refs/heads/', '', 1)
|
@@ -1002,7 +994,7 @@ class _ParsedIssueNumberArgument(object):
|
|
1002
994
|
self.issue = issue
|
1003
995
|
self.patchset = patchset
|
1004
996
|
self.hostname = hostname
|
1005
|
-
assert codereview in (None, '
|
997
|
+
assert codereview in (None, 'gerrit', 'rietveld')
|
1006
998
|
self.codereview = codereview
|
1007
999
|
|
1008
1000
|
@property
|
@@ -1029,22 +1021,21 @@ def ParseIssueNumberArgument(arg, codereview=None):
|
|
1029
1021
|
parsed = _CODEREVIEW_IMPLEMENTATIONS[codereview].ParseIssueURL(parsed_url)
|
1030
1022
|
return parsed or fail_result
|
1031
1023
|
|
1032
|
-
|
1033
|
-
for name, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems():
|
1034
|
-
parsed = cls.ParseIssueURL(parsed_url)
|
1035
|
-
if parsed is not None:
|
1036
|
-
results[name] = parsed
|
1024
|
+
return _GerritChangelistImpl.ParseIssueURL(parsed_url) or fail_result
|
1037
1025
|
|
1038
|
-
if not results:
|
1039
|
-
return fail_result
|
1040
|
-
if len(results) == 1:
|
1041
|
-
return results.values()[0]
|
1042
1026
|
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1027
|
+
def _create_description_from_log(args):
|
1028
|
+
"""Pulls out the commit log to use as a base for the CL description."""
|
1029
|
+
log_args = []
|
1030
|
+
if len(args) == 1 and not args[0].endswith('.'):
|
1031
|
+
log_args = [args[0] + '..']
|
1032
|
+
elif len(args) == 1 and args[0].endswith('...'):
|
1033
|
+
log_args = [args[0][:-1]]
|
1034
|
+
elif len(args) == 2:
|
1035
|
+
log_args = [args[0] + '..' + args[1]]
|
1036
|
+
else:
|
1037
|
+
log_args = args[:] # Hope for the best!
|
1038
|
+
return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args)
|
1048
1039
|
|
1049
1040
|
|
1050
1041
|
class GerritChangeNotExists(Exception):
|
@@ -1059,7 +1050,7 @@ class GerritChangeNotExists(Exception):
|
|
1059
1050
|
|
1060
1051
|
|
1061
1052
|
_CommentSummary = collections.namedtuple(
|
1062
|
-
'_CommentSummary', ['date', 'message', 'sender',
|
1053
|
+
'_CommentSummary', ['date', 'message', 'sender', 'autogenerated',
|
1063
1054
|
# TODO(tandrii): these two aren't known in Gerrit.
|
1064
1055
|
'approval', 'disapproval'])
|
1065
1056
|
|
@@ -1113,6 +1104,7 @@ class Changelist(object):
|
|
1113
1104
|
self.cc = None
|
1114
1105
|
self.more_cc = []
|
1115
1106
|
self._remote = None
|
1107
|
+
self._cached_remote_url = (False, None) # (is_cached, value)
|
1116
1108
|
|
1117
1109
|
self._codereview_impl = None
|
1118
1110
|
self._codereview = None
|
@@ -1122,7 +1114,9 @@ class Changelist(object):
|
|
1122
1114
|
|
1123
1115
|
def _load_codereview_impl(self, codereview=None, **kwargs):
|
1124
1116
|
if codereview:
|
1125
|
-
assert codereview in _CODEREVIEW_IMPLEMENTATIONS
|
1117
|
+
assert codereview in _CODEREVIEW_IMPLEMENTATIONS, (
|
1118
|
+
'codereview {} not in {}'.format(codereview,
|
1119
|
+
_CODEREVIEW_IMPLEMENTATIONS))
|
1126
1120
|
cls = _CODEREVIEW_IMPLEMENTATIONS[codereview]
|
1127
1121
|
self._codereview = codereview
|
1128
1122
|
self._codereview_impl = cls(self, **kwargs)
|
@@ -1143,9 +1137,9 @@ class Changelist(object):
|
|
1143
1137
|
self.issue = int(issue)
|
1144
1138
|
return
|
1145
1139
|
|
1146
|
-
# No issue is set for this branch, so
|
1140
|
+
# No issue is set for this branch, so default to gerrit.
|
1147
1141
|
return self._load_codereview_impl(
|
1148
|
-
codereview='gerrit'
|
1142
|
+
codereview='gerrit',
|
1149
1143
|
**kwargs)
|
1150
1144
|
|
1151
1145
|
def IsGerrit(self):
|
@@ -1342,14 +1336,47 @@ class Changelist(object):
|
|
1342
1336
|
|
1343
1337
|
Returns None if there is no remote.
|
1344
1338
|
"""
|
1339
|
+
is_cached, value = self._cached_remote_url
|
1340
|
+
if is_cached:
|
1341
|
+
return value
|
1342
|
+
|
1345
1343
|
remote, _ = self.GetRemoteBranch()
|
1346
1344
|
url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip()
|
1347
1345
|
|
1348
|
-
#
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1346
|
+
# Check if the remote url can be parsed as an URL.
|
1347
|
+
host = urlparse.urlparse(url).netloc
|
1348
|
+
if host:
|
1349
|
+
self._cached_remote_url = (True, url)
|
1350
|
+
return url
|
1351
|
+
|
1352
|
+
# If it cannot be parsed as an url, assume it is a local directory, probably
|
1353
|
+
# a git cache.
|
1354
|
+
logging.warning('"%s" doesn\'t appear to point to a git host. '
|
1355
|
+
'Interpreting it as a local directory.', url)
|
1356
|
+
if not os.path.isdir(url):
|
1357
|
+
logging.error(
|
1358
|
+
'Remote "%s" for branch "%s" points to "%s", but it doesn\'t exist.',
|
1359
|
+
remote, url, self.GetBranch())
|
1360
|
+
return None
|
1361
|
+
|
1362
|
+
cache_path = url
|
1363
|
+
url = RunGit(['config', 'remote.%s.url' % remote],
|
1364
|
+
error_ok=True,
|
1365
|
+
cwd=url).strip()
|
1366
|
+
|
1367
|
+
host = urlparse.urlparse(url).netloc
|
1368
|
+
if not host:
|
1369
|
+
logging.error(
|
1370
|
+
'Remote "%(remote)s" for branch "%(branch)s" points to '
|
1371
|
+
'"%(cache_path)s", but it is misconfigured.\n'
|
1372
|
+
'"%(cache_path)s" must be a git repo and must have a remote named '
|
1373
|
+
'"%(remote)s" pointing to the git host.', {
|
1374
|
+
'remote': remote,
|
1375
|
+
'cache_path': cache_path,
|
1376
|
+
'branch': self.GetBranch()})
|
1377
|
+
return None
|
1378
|
+
|
1379
|
+
self._cached_remote_url = (True, url)
|
1353
1380
|
return url
|
1354
1381
|
|
1355
1382
|
def GetIssue(self):
|
@@ -1547,6 +1574,7 @@ class Changelist(object):
|
|
1547
1574
|
|
1548
1575
|
def CMDUpload(self, options, git_diff_args, orig_args):
|
1549
1576
|
"""Uploads a change to codereview."""
|
1577
|
+
assert self.IsGerrit()
|
1550
1578
|
custom_cl_base = None
|
1551
1579
|
if git_diff_args:
|
1552
1580
|
custom_cl_base = base_branch = git_diff_args[0]
|
@@ -1558,15 +1586,6 @@ class Changelist(object):
|
|
1558
1586
|
base_branch = self.GetCommonAncestorWithUpstream()
|
1559
1587
|
git_diff_args = [base_branch, 'HEAD']
|
1560
1588
|
|
1561
|
-
# Warn about Rietveld deprecation for initial uploads to Rietveld.
|
1562
|
-
if not self.IsGerrit() and not self.GetIssue():
|
1563
|
-
print('=====================================')
|
1564
|
-
print('NOTICE: Rietveld is being deprecated. '
|
1565
|
-
'You can upload changes to Gerrit with')
|
1566
|
-
print(' git cl upload --gerrit')
|
1567
|
-
print('or set Gerrit to be your default code review tool with')
|
1568
|
-
print(' git config gerrit.host true')
|
1569
|
-
print('=====================================')
|
1570
1589
|
|
1571
1590
|
# Fast best-effort checks to abort before running potentially
|
1572
1591
|
# expensive hooks if uploading is likely to fail anyway. Passing these
|
@@ -1600,33 +1619,9 @@ class Changelist(object):
|
|
1600
1619
|
options.reviewers = hook_results.reviewers.split(',')
|
1601
1620
|
self.ExtendCC(hook_results.more_cc)
|
1602
1621
|
|
1603
|
-
# TODO(tandrii): Checking local patchset against remote patchset is only
|
1604
|
-
# supported for Rietveld. Extend it to Gerrit or remove it completely.
|
1605
|
-
if self.GetIssue() and not self.IsGerrit():
|
1606
|
-
latest_patchset = self.GetMostRecentPatchset()
|
1607
|
-
local_patchset = self.GetPatchset()
|
1608
|
-
if (latest_patchset and local_patchset and
|
1609
|
-
local_patchset != latest_patchset):
|
1610
|
-
print('The last upload made from this repository was patchset #%d but '
|
1611
|
-
'the most recent patchset on the server is #%d.'
|
1612
|
-
% (local_patchset, latest_patchset))
|
1613
|
-
print('Uploading will still work, but if you\'ve uploaded to this '
|
1614
|
-
'issue from another machine or branch the patch you\'re '
|
1615
|
-
'uploading now might not include those changes.')
|
1616
|
-
confirm_or_exit(action='upload')
|
1617
|
-
|
1618
1622
|
print_stats(git_diff_args)
|
1619
1623
|
ret = self.CMDUploadChange(options, git_diff_args, custom_cl_base, change)
|
1620
1624
|
if not ret:
|
1621
|
-
if self.IsGerrit():
|
1622
|
-
self.SetLabels(options.enable_auto_submit, options.use_commit_queue,
|
1623
|
-
options.cq_dry_run);
|
1624
|
-
else:
|
1625
|
-
if options.use_commit_queue:
|
1626
|
-
self.SetCQState(_CQState.COMMIT)
|
1627
|
-
elif options.cq_dry_run:
|
1628
|
-
self.SetCQState(_CQState.DRY_RUN)
|
1629
|
-
|
1630
1625
|
_git_set_branch_config_value('last-upload-hash',
|
1631
1626
|
RunGit(['rev-parse', 'HEAD']).strip())
|
1632
1627
|
# Run post upload hooks, if specified.
|
@@ -1650,41 +1645,6 @@ class Changelist(object):
|
|
1650
1645
|
ret = upload_branch_deps(self, orig_args)
|
1651
1646
|
return ret
|
1652
1647
|
|
1653
|
-
def SetLabels(self, enable_auto_submit, use_commit_queue, cq_dry_run):
|
1654
|
-
"""Sets labels on the change based on the provided flags.
|
1655
|
-
|
1656
|
-
Sets labels if issue is already uploaded and known, else returns without
|
1657
|
-
doing anything.
|
1658
|
-
|
1659
|
-
Args:
|
1660
|
-
enable_auto_submit: Sets Auto-Submit+1 on the change.
|
1661
|
-
use_commit_queue: Sets Commit-Queue+2 on the change.
|
1662
|
-
cq_dry_run: Sets Commit-Queue+1 on the change. Overrides Commit-Queue+2 if
|
1663
|
-
both use_commit_queue and cq_dry_run are true.
|
1664
|
-
"""
|
1665
|
-
if not self.GetIssue():
|
1666
|
-
return
|
1667
|
-
try:
|
1668
|
-
self._codereview_impl.SetLabels(enable_auto_submit, use_commit_queue,
|
1669
|
-
cq_dry_run)
|
1670
|
-
return 0
|
1671
|
-
except KeyboardInterrupt:
|
1672
|
-
raise
|
1673
|
-
except:
|
1674
|
-
labels = []
|
1675
|
-
if enable_auto_submit:
|
1676
|
-
labels.append('Auto-Submit')
|
1677
|
-
if use_commit_queue or cq_dry_run:
|
1678
|
-
labels.append('Commit-Queue')
|
1679
|
-
print('WARNING: Failed to set label(s) on your change: %s\n'
|
1680
|
-
'Either:\n'
|
1681
|
-
' * Your project does not have the above label(s),\n'
|
1682
|
-
' * You don\'t have permission to set the above label(s),\n'
|
1683
|
-
' * There\'s a bug in this code (see stack trace below).\n' %
|
1684
|
-
(', '.join(labels)))
|
1685
|
-
# Still raise exception so that stack trace is printed.
|
1686
|
-
raise
|
1687
|
-
|
1688
1648
|
def SetCQState(self, new_state):
|
1689
1649
|
"""Updates the CQ state for the latest patchset.
|
1690
1650
|
|
@@ -1878,13 +1838,6 @@ class _ChangelistCodereviewBase(object):
|
|
1878
1838
|
"""Uploads a change to codereview."""
|
1879
1839
|
raise NotImplementedError()
|
1880
1840
|
|
1881
|
-
def SetLabels(self, enable_auto_submit, use_commit_queue, cq_dry_run):
|
1882
|
-
"""Sets labels on the change based on the provided flags.
|
1883
|
-
|
1884
|
-
Issue must have been already uploaded and known.
|
1885
|
-
"""
|
1886
|
-
raise NotImplementedError()
|
1887
|
-
|
1888
1841
|
def SetCQState(self, new_state):
|
1889
1842
|
"""Updates the CQ state for the latest patchset.
|
1890
1843
|
|
@@ -1906,446 +1859,6 @@ class _ChangelistCodereviewBase(object):
|
|
1906
1859
|
raise NotImplementedError()
|
1907
1860
|
|
1908
1861
|
|
1909
|
-
class _RietveldChangelistImpl(_ChangelistCodereviewBase):
|
1910
|
-
|
1911
|
-
def __init__(self, changelist, auth_config=None, codereview_host=None):
|
1912
|
-
super(_RietveldChangelistImpl, self).__init__(changelist)
|
1913
|
-
assert settings, 'must be initialized in _ChangelistCodereviewBase'
|
1914
|
-
if not codereview_host:
|
1915
|
-
settings.GetDefaultServerUrl()
|
1916
|
-
|
1917
|
-
self._rietveld_server = codereview_host
|
1918
|
-
self._auth_config = auth_config or auth.make_auth_config()
|
1919
|
-
self._props = None
|
1920
|
-
self._rpc_server = None
|
1921
|
-
|
1922
|
-
def GetCodereviewServer(self):
|
1923
|
-
if not self._rietveld_server:
|
1924
|
-
# If we're on a branch then get the server potentially associated
|
1925
|
-
# with that branch.
|
1926
|
-
if self.GetIssue():
|
1927
|
-
self._rietveld_server = gclient_utils.UpgradeToHttps(
|
1928
|
-
self._GitGetBranchConfigValue(self.CodereviewServerConfigKey()))
|
1929
|
-
if not self._rietveld_server:
|
1930
|
-
self._rietveld_server = settings.GetDefaultServerUrl()
|
1931
|
-
return self._rietveld_server
|
1932
|
-
|
1933
|
-
def EnsureAuthenticated(self, force, refresh=False):
|
1934
|
-
"""Best effort check that user is authenticated with Rietveld server."""
|
1935
|
-
if self._auth_config.use_oauth2:
|
1936
|
-
authenticator = auth.get_authenticator_for_host(
|
1937
|
-
self.GetCodereviewServer(), self._auth_config)
|
1938
|
-
if not authenticator.has_cached_credentials():
|
1939
|
-
raise auth.LoginRequiredError(self.GetCodereviewServer())
|
1940
|
-
if refresh:
|
1941
|
-
authenticator.get_access_token()
|
1942
|
-
|
1943
|
-
def EnsureCanUploadPatchset(self, force):
|
1944
|
-
# No checks for Rietveld because we are deprecating Rietveld.
|
1945
|
-
pass
|
1946
|
-
|
1947
|
-
def FetchDescription(self, force=False):
|
1948
|
-
issue = self.GetIssue()
|
1949
|
-
assert issue
|
1950
|
-
try:
|
1951
|
-
return self.RpcServer().get_description(issue, force=force).strip()
|
1952
|
-
except urllib2.HTTPError as e:
|
1953
|
-
if e.code == 404:
|
1954
|
-
DieWithError(
|
1955
|
-
('\nWhile fetching the description for issue %d, received a '
|
1956
|
-
'404 (not found)\n'
|
1957
|
-
'error. It is likely that you deleted this '
|
1958
|
-
'issue on the server. If this is the\n'
|
1959
|
-
'case, please run\n\n'
|
1960
|
-
' git cl issue 0\n\n'
|
1961
|
-
'to clear the association with the deleted issue. Then run '
|
1962
|
-
'this command again.') % issue)
|
1963
|
-
else:
|
1964
|
-
DieWithError(
|
1965
|
-
'\nFailed to fetch issue description. HTTP error %d' % e.code)
|
1966
|
-
except urllib2.URLError as e:
|
1967
|
-
print('Warning: Failed to retrieve CL description due to network '
|
1968
|
-
'failure.', file=sys.stderr)
|
1969
|
-
return ''
|
1970
|
-
|
1971
|
-
def GetMostRecentPatchset(self):
|
1972
|
-
return self.GetIssueProperties()['patchsets'][-1]
|
1973
|
-
|
1974
|
-
def GetIssueProperties(self):
|
1975
|
-
if self._props is None:
|
1976
|
-
issue = self.GetIssue()
|
1977
|
-
if not issue:
|
1978
|
-
self._props = {}
|
1979
|
-
else:
|
1980
|
-
self._props = self.RpcServer().get_issue_properties(issue, True)
|
1981
|
-
return self._props
|
1982
|
-
|
1983
|
-
def CannotTriggerTryJobReason(self):
|
1984
|
-
props = self.GetIssueProperties()
|
1985
|
-
if not props:
|
1986
|
-
return 'Rietveld doesn\'t know about your issue %s' % self.GetIssue()
|
1987
|
-
if props.get('closed'):
|
1988
|
-
return 'CL %s is closed' % self.GetIssue()
|
1989
|
-
if props.get('private'):
|
1990
|
-
return 'CL %s is private' % self.GetIssue()
|
1991
|
-
return None
|
1992
|
-
|
1993
|
-
def GetTryJobProperties(self, patchset=None):
|
1994
|
-
"""Returns dictionary of properties to launch try job."""
|
1995
|
-
project = (self.GetIssueProperties() or {}).get('project')
|
1996
|
-
return {
|
1997
|
-
'issue': self.GetIssue(),
|
1998
|
-
'patch_project': project,
|
1999
|
-
'patch_storage': 'rietveld',
|
2000
|
-
'patchset': patchset or self.GetPatchset(),
|
2001
|
-
'rietveld': self.GetCodereviewServer(),
|
2002
|
-
}
|
2003
|
-
|
2004
|
-
def GetIssueOwner(self):
|
2005
|
-
return (self.GetIssueProperties() or {}).get('owner_email')
|
2006
|
-
|
2007
|
-
def GetReviewers(self):
|
2008
|
-
return (self.GetIssueProperties() or {}).get('reviewers')
|
2009
|
-
|
2010
|
-
def AddComment(self, message, publish=None):
|
2011
|
-
return self.RpcServer().add_comment(self.GetIssue(), message)
|
2012
|
-
|
2013
|
-
def GetCommentsSummary(self, _readable=True):
|
2014
|
-
summary = []
|
2015
|
-
for message in self.GetIssueProperties().get('messages', []):
|
2016
|
-
date = datetime.datetime.strptime(message['date'], '%Y-%m-%d %H:%M:%S.%f')
|
2017
|
-
summary.append(_CommentSummary(
|
2018
|
-
date=date,
|
2019
|
-
disapproval=bool(message['disapproval']),
|
2020
|
-
approval=bool(message['approval']),
|
2021
|
-
sender=message['sender'],
|
2022
|
-
message=message['text'],
|
2023
|
-
))
|
2024
|
-
return summary
|
2025
|
-
|
2026
|
-
def GetStatus(self):
|
2027
|
-
"""Applies a rough heuristic to give a simple summary of an issue's review
|
2028
|
-
or CQ status, assuming adherence to a common workflow.
|
2029
|
-
|
2030
|
-
Returns None if no issue for this branch, or one of the following keywords:
|
2031
|
-
* 'error' - error from review tool (including deleted issues)
|
2032
|
-
* 'unsent' - not sent for review
|
2033
|
-
* 'waiting' - waiting for review
|
2034
|
-
* 'reply' - waiting for owner to reply to review
|
2035
|
-
* 'not lgtm' - Code-Review label has been set negatively
|
2036
|
-
* 'lgtm' - LGTM from at least one approved reviewer
|
2037
|
-
* 'commit' - in the commit queue
|
2038
|
-
* 'closed' - closed
|
2039
|
-
"""
|
2040
|
-
if not self.GetIssue():
|
2041
|
-
return None
|
2042
|
-
|
2043
|
-
try:
|
2044
|
-
props = self.GetIssueProperties()
|
2045
|
-
except urllib2.HTTPError:
|
2046
|
-
return 'error'
|
2047
|
-
|
2048
|
-
if props.get('closed'):
|
2049
|
-
# Issue is closed.
|
2050
|
-
return 'closed'
|
2051
|
-
if props.get('commit') and not props.get('cq_dry_run', False):
|
2052
|
-
# Issue is in the commit queue.
|
2053
|
-
return 'commit'
|
2054
|
-
|
2055
|
-
messages = props.get('messages') or []
|
2056
|
-
if not messages:
|
2057
|
-
# No message was sent.
|
2058
|
-
return 'unsent'
|
2059
|
-
|
2060
|
-
if get_approving_reviewers(props):
|
2061
|
-
return 'lgtm'
|
2062
|
-
elif get_approving_reviewers(props, disapproval=True):
|
2063
|
-
return 'not lgtm'
|
2064
|
-
|
2065
|
-
# Skip CQ messages that don't require owner's action.
|
2066
|
-
while messages and messages[-1]['sender'] == COMMIT_BOT_EMAIL:
|
2067
|
-
if 'Dry run:' in messages[-1]['text']:
|
2068
|
-
messages.pop()
|
2069
|
-
elif 'The CQ bit was unchecked' in messages[-1]['text']:
|
2070
|
-
# This message always follows prior messages from CQ,
|
2071
|
-
# so skip this too.
|
2072
|
-
messages.pop()
|
2073
|
-
else:
|
2074
|
-
# This is probably a CQ messages warranting user attention.
|
2075
|
-
break
|
2076
|
-
|
2077
|
-
if messages[-1]['sender'] != props.get('owner_email'):
|
2078
|
-
# Non-LGTM reply from non-owner and not CQ bot.
|
2079
|
-
return 'reply'
|
2080
|
-
return 'waiting'
|
2081
|
-
|
2082
|
-
def UpdateDescriptionRemote(self, description, force=False):
|
2083
|
-
self.RpcServer().update_description(self.GetIssue(), description)
|
2084
|
-
|
2085
|
-
def CloseIssue(self):
|
2086
|
-
return self.RpcServer().close_issue(self.GetIssue())
|
2087
|
-
|
2088
|
-
def SetFlag(self, flag, value):
|
2089
|
-
return self.SetFlags({flag: value})
|
2090
|
-
|
2091
|
-
def SetFlags(self, flags):
|
2092
|
-
"""Sets flags on this CL/patchset in Rietveld.
|
2093
|
-
"""
|
2094
|
-
patchset = self.GetPatchset() or self.GetMostRecentPatchset()
|
2095
|
-
try:
|
2096
|
-
return self.RpcServer().set_flags(
|
2097
|
-
self.GetIssue(), patchset, flags)
|
2098
|
-
except urllib2.HTTPError as e:
|
2099
|
-
if e.code == 404:
|
2100
|
-
DieWithError('The issue %s doesn\'t exist.' % self.GetIssue())
|
2101
|
-
if e.code == 403:
|
2102
|
-
DieWithError(
|
2103
|
-
('Access denied to issue %s. Maybe the patchset %s doesn\'t '
|
2104
|
-
'match?') % (self.GetIssue(), patchset))
|
2105
|
-
raise
|
2106
|
-
|
2107
|
-
def RpcServer(self):
|
2108
|
-
"""Returns an upload.RpcServer() to access this review's rietveld instance.
|
2109
|
-
"""
|
2110
|
-
if not self._rpc_server:
|
2111
|
-
self._rpc_server = rietveld.CachingRietveld(
|
2112
|
-
self.GetCodereviewServer(),
|
2113
|
-
self._auth_config)
|
2114
|
-
return self._rpc_server
|
2115
|
-
|
2116
|
-
@classmethod
|
2117
|
-
def IssueConfigKey(cls):
|
2118
|
-
return 'rietveldissue'
|
2119
|
-
|
2120
|
-
@classmethod
|
2121
|
-
def PatchsetConfigKey(cls):
|
2122
|
-
return 'rietveldpatchset'
|
2123
|
-
|
2124
|
-
@classmethod
|
2125
|
-
def CodereviewServerConfigKey(cls):
|
2126
|
-
return 'rietveldserver'
|
2127
|
-
|
2128
|
-
def SetLabels(self, enable_auto_submit, use_commit_queue, cq_dry_run):
|
2129
|
-
raise NotImplementedError()
|
2130
|
-
|
2131
|
-
def SetCQState(self, new_state):
|
2132
|
-
props = self.GetIssueProperties()
|
2133
|
-
if props.get('private'):
|
2134
|
-
DieWithError('Cannot set-commit on private issue')
|
2135
|
-
|
2136
|
-
if new_state == _CQState.COMMIT:
|
2137
|
-
self.SetFlags({'commit': '1', 'cq_dry_run': '0'})
|
2138
|
-
elif new_state == _CQState.NONE:
|
2139
|
-
self.SetFlags({'commit': '0', 'cq_dry_run': '0'})
|
2140
|
-
else:
|
2141
|
-
assert new_state == _CQState.DRY_RUN
|
2142
|
-
self.SetFlags({'commit': '1', 'cq_dry_run': '1'})
|
2143
|
-
|
2144
|
-
def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit,
|
2145
|
-
directory, force):
|
2146
|
-
# PatchIssue should never be called with a dirty tree. It is up to the
|
2147
|
-
# caller to check this, but just in case we assert here since the
|
2148
|
-
# consequences of the caller not checking this could be dire.
|
2149
|
-
assert(not git_common.is_dirty_git_tree('apply'))
|
2150
|
-
assert(parsed_issue_arg.valid)
|
2151
|
-
self._changelist.issue = parsed_issue_arg.issue
|
2152
|
-
if parsed_issue_arg.hostname:
|
2153
|
-
self._rietveld_server = 'https://%s' % parsed_issue_arg.hostname
|
2154
|
-
|
2155
|
-
patchset = parsed_issue_arg.patchset or self.GetMostRecentPatchset()
|
2156
|
-
patchset_object = self.RpcServer().get_patch(self.GetIssue(), patchset)
|
2157
|
-
scm_obj = checkout.GitCheckout(settings.GetRoot(), None, None, None, None)
|
2158
|
-
try:
|
2159
|
-
scm_obj.apply_patch(patchset_object)
|
2160
|
-
except Exception as e:
|
2161
|
-
print(str(e))
|
2162
|
-
return 1
|
2163
|
-
|
2164
|
-
# If we had an issue, commit the current state and register the issue.
|
2165
|
-
if not nocommit:
|
2166
|
-
self.SetIssue(self.GetIssue())
|
2167
|
-
self.SetPatchset(patchset)
|
2168
|
-
RunGit(['commit', '-m', (self.GetDescription() + '\n\n' +
|
2169
|
-
'patch from issue %(i)s at patchset '
|
2170
|
-
'%(p)s (http://crrev.com/%(i)s#ps%(p)s)'
|
2171
|
-
% {'i': self.GetIssue(), 'p': patchset})])
|
2172
|
-
print('Committed patch locally.')
|
2173
|
-
else:
|
2174
|
-
print('Patch applied to index.')
|
2175
|
-
return 0
|
2176
|
-
|
2177
|
-
@staticmethod
|
2178
|
-
def ParseIssueURL(parsed_url):
|
2179
|
-
if not parsed_url.scheme or not parsed_url.scheme.startswith('http'):
|
2180
|
-
return None
|
2181
|
-
# Rietveld patch: https://domain/<number>/#ps<patchset>
|
2182
|
-
match = re.match(r'/(\d+)/$', parsed_url.path)
|
2183
|
-
match2 = re.match(r'ps(\d+)$', parsed_url.fragment)
|
2184
|
-
if match and match2:
|
2185
|
-
return _ParsedIssueNumberArgument(
|
2186
|
-
issue=int(match.group(1)),
|
2187
|
-
patchset=int(match2.group(1)),
|
2188
|
-
hostname=parsed_url.netloc,
|
2189
|
-
codereview='rietveld')
|
2190
|
-
# Typical url: https://domain/<issue_number>[/[other]]
|
2191
|
-
match = re.match('/(\d+)(/.*)?$', parsed_url.path)
|
2192
|
-
if match:
|
2193
|
-
return _ParsedIssueNumberArgument(
|
2194
|
-
issue=int(match.group(1)),
|
2195
|
-
hostname=parsed_url.netloc,
|
2196
|
-
codereview='rietveld')
|
2197
|
-
# Rietveld patch: https://domain/download/issue<number>_<patchset>.diff
|
2198
|
-
match = re.match(r'/download/issue(\d+)_(\d+).diff$', parsed_url.path)
|
2199
|
-
if match:
|
2200
|
-
return _ParsedIssueNumberArgument(
|
2201
|
-
issue=int(match.group(1)),
|
2202
|
-
patchset=int(match.group(2)),
|
2203
|
-
hostname=parsed_url.netloc,
|
2204
|
-
codereview='rietveld')
|
2205
|
-
return None
|
2206
|
-
|
2207
|
-
def CMDUploadChange(self, options, args, custom_cl_base, change):
|
2208
|
-
"""Upload the patch to Rietveld."""
|
2209
|
-
upload_args = ['--assume_yes'] # Don't ask about untracked files.
|
2210
|
-
upload_args.extend(['--server', self.GetCodereviewServer()])
|
2211
|
-
upload_args.extend(auth.auth_config_to_command_options(self._auth_config))
|
2212
|
-
if options.emulate_svn_auto_props:
|
2213
|
-
upload_args.append('--emulate_svn_auto_props')
|
2214
|
-
|
2215
|
-
change_desc = None
|
2216
|
-
|
2217
|
-
if options.email is not None:
|
2218
|
-
upload_args.extend(['--email', options.email])
|
2219
|
-
|
2220
|
-
if self.GetIssue():
|
2221
|
-
if options.title is not None:
|
2222
|
-
upload_args.extend(['--title', options.title])
|
2223
|
-
if options.message:
|
2224
|
-
upload_args.extend(['--message', options.message])
|
2225
|
-
upload_args.extend(['--issue', str(self.GetIssue())])
|
2226
|
-
print('This branch is associated with issue %s. '
|
2227
|
-
'Adding patch to that issue.' % self.GetIssue())
|
2228
|
-
else:
|
2229
|
-
if options.title is not None:
|
2230
|
-
upload_args.extend(['--title', options.title])
|
2231
|
-
if options.message:
|
2232
|
-
message = options.message
|
2233
|
-
else:
|
2234
|
-
message = CreateDescriptionFromLog(args)
|
2235
|
-
if options.title:
|
2236
|
-
message = options.title + '\n\n' + message
|
2237
|
-
change_desc = ChangeDescription(message)
|
2238
|
-
if options.reviewers or options.add_owners_to:
|
2239
|
-
change_desc.update_reviewers(options.reviewers, options.tbrs,
|
2240
|
-
options.add_owners_to, change)
|
2241
|
-
if not options.force:
|
2242
|
-
change_desc.prompt(bug=options.bug, git_footer=False)
|
2243
|
-
|
2244
|
-
if not change_desc.description:
|
2245
|
-
print('Description is empty; aborting.')
|
2246
|
-
return 1
|
2247
|
-
|
2248
|
-
upload_args.extend(['--message', change_desc.description])
|
2249
|
-
if change_desc.get_reviewers():
|
2250
|
-
upload_args.append('--reviewers=%s' % ','.join(
|
2251
|
-
change_desc.get_reviewers()))
|
2252
|
-
if options.send_mail:
|
2253
|
-
if not change_desc.get_reviewers():
|
2254
|
-
DieWithError("Must specify reviewers to send email.", change_desc)
|
2255
|
-
upload_args.append('--send_mail')
|
2256
|
-
|
2257
|
-
# We check this before applying rietveld.private assuming that in
|
2258
|
-
# rietveld.cc only addresses which we can send private CLs to are listed
|
2259
|
-
# if rietveld.private is set, and so we should ignore rietveld.cc only
|
2260
|
-
# when --private is specified explicitly on the command line.
|
2261
|
-
if options.private:
|
2262
|
-
logging.warn('rietveld.cc is ignored since private flag is specified. '
|
2263
|
-
'You need to review and add them manually if necessary.')
|
2264
|
-
cc = self.GetCCListWithoutDefault()
|
2265
|
-
else:
|
2266
|
-
cc = self.GetCCList()
|
2267
|
-
cc = ','.join(filter(None, (cc, ','.join(options.cc))))
|
2268
|
-
if change_desc.get_cced():
|
2269
|
-
cc = ','.join(filter(None, (cc, ','.join(change_desc.get_cced()))))
|
2270
|
-
if cc:
|
2271
|
-
upload_args.extend(['--cc', cc])
|
2272
|
-
|
2273
|
-
if options.private or settings.GetDefaultPrivateFlag() == "True":
|
2274
|
-
upload_args.append('--private')
|
2275
|
-
|
2276
|
-
# Include the upstream repo's URL in the change -- this is useful for
|
2277
|
-
# projects that have their source spread across multiple repos.
|
2278
|
-
remote_url = self.GetGitBaseUrlFromConfig()
|
2279
|
-
if not remote_url:
|
2280
|
-
if self.GetRemoteUrl() and '/' in self.GetUpstreamBranch():
|
2281
|
-
remote_url = '%s@%s' % (self.GetRemoteUrl(),
|
2282
|
-
self.GetUpstreamBranch().split('/')[-1])
|
2283
|
-
if remote_url:
|
2284
|
-
remote, remote_branch = self.GetRemoteBranch()
|
2285
|
-
target_ref = GetTargetRef(remote, remote_branch, options.target_branch)
|
2286
|
-
if target_ref:
|
2287
|
-
upload_args.extend(['--target_ref', target_ref])
|
2288
|
-
|
2289
|
-
# Look for dependent patchsets. See crbug.com/480453 for more details.
|
2290
|
-
remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
|
2291
|
-
upstream_branch = ShortBranchName(upstream_branch)
|
2292
|
-
if remote is '.':
|
2293
|
-
# A local branch is being tracked.
|
2294
|
-
local_branch = upstream_branch
|
2295
|
-
if settings.GetIsSkipDependencyUpload(local_branch):
|
2296
|
-
print()
|
2297
|
-
print('Skipping dependency patchset upload because git config '
|
2298
|
-
'branch.%s.skip-deps-uploads is set to True.' % local_branch)
|
2299
|
-
print()
|
2300
|
-
else:
|
2301
|
-
auth_config = auth.extract_auth_config_from_options(options)
|
2302
|
-
branch_cl = Changelist(branchref='refs/heads/'+local_branch,
|
2303
|
-
auth_config=auth_config)
|
2304
|
-
branch_cl_issue_url = branch_cl.GetIssueURL()
|
2305
|
-
branch_cl_issue = branch_cl.GetIssue()
|
2306
|
-
branch_cl_patchset = branch_cl.GetPatchset()
|
2307
|
-
if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset:
|
2308
|
-
upload_args.extend(
|
2309
|
-
['--depends_on_patchset', '%s:%s' % (
|
2310
|
-
branch_cl_issue, branch_cl_patchset)])
|
2311
|
-
print(
|
2312
|
-
'\n'
|
2313
|
-
'The current branch (%s) is tracking a local branch (%s) with '
|
2314
|
-
'an associated CL.\n'
|
2315
|
-
'Adding %s/#ps%s as a dependency patchset.\n'
|
2316
|
-
'\n' % (self.GetBranch(), local_branch, branch_cl_issue_url,
|
2317
|
-
branch_cl_patchset))
|
2318
|
-
|
2319
|
-
project = settings.GetProject()
|
2320
|
-
if project:
|
2321
|
-
upload_args.extend(['--project', project])
|
2322
|
-
else:
|
2323
|
-
print()
|
2324
|
-
print('WARNING: Uploading without a project specified. Please ensure '
|
2325
|
-
'your repo\'s codereview.settings has a "PROJECT: foo" line.')
|
2326
|
-
print()
|
2327
|
-
|
2328
|
-
try:
|
2329
|
-
upload_args = ['upload'] + upload_args + args
|
2330
|
-
logging.info('upload.RealMain(%s)', upload_args)
|
2331
|
-
issue, patchset = upload.RealMain(upload_args)
|
2332
|
-
issue = int(issue)
|
2333
|
-
patchset = int(patchset)
|
2334
|
-
except KeyboardInterrupt:
|
2335
|
-
sys.exit(1)
|
2336
|
-
except:
|
2337
|
-
# If we got an exception after the user typed a description for their
|
2338
|
-
# change, back up the description before re-raising.
|
2339
|
-
if change_desc:
|
2340
|
-
SaveDescriptionBackup(change_desc)
|
2341
|
-
raise
|
2342
|
-
|
2343
|
-
if not self.GetIssue():
|
2344
|
-
self.SetIssue(issue)
|
2345
|
-
self.SetPatchset(patchset)
|
2346
|
-
return 0
|
2347
|
-
|
2348
|
-
|
2349
1862
|
class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
2350
1863
|
def __init__(self, changelist, auth_config=None, codereview_host=None):
|
2351
1864
|
# auth_config is Rietveld thing, kept here to preserve interface only.
|
@@ -2378,7 +1891,10 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2378
1891
|
|
2379
1892
|
def _GetGitHost(self):
|
2380
1893
|
"""Returns git host to be used when uploading change to Gerrit."""
|
2381
|
-
|
1894
|
+
remote_url = self.GetRemoteUrl()
|
1895
|
+
if not remote_url:
|
1896
|
+
return None
|
1897
|
+
return urlparse.urlparse(remote_url).netloc
|
2382
1898
|
|
2383
1899
|
def GetCodereviewServer(self):
|
2384
1900
|
if not self._gerrit_server:
|
@@ -2398,6 +1914,37 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2398
1914
|
self._gerrit_server = 'https://%s' % self._gerrit_host
|
2399
1915
|
return self._gerrit_server
|
2400
1916
|
|
1917
|
+
def _GetGerritProject(self):
|
1918
|
+
"""Returns Gerrit project name based on remote git URL."""
|
1919
|
+
remote_url = self.GetRemoteUrl()
|
1920
|
+
if remote_url is None:
|
1921
|
+
logging.warn('can\'t detect Gerrit project.')
|
1922
|
+
return None
|
1923
|
+
project = urlparse.urlparse(remote_url).path.strip('/')
|
1924
|
+
if project.endswith('.git'):
|
1925
|
+
project = project[:-len('.git')]
|
1926
|
+
# *.googlesource.com hosts ensure that Git/Gerrit projects don't start with
|
1927
|
+
# 'a/' prefix, because 'a/' prefix is used to force authentication in
|
1928
|
+
# gitiles/git-over-https protocol. E.g.,
|
1929
|
+
# https://chromium.googlesource.com/a/v8/v8 refers to the same repo/project
|
1930
|
+
# as
|
1931
|
+
# https://chromium.googlesource.com/v8/v8
|
1932
|
+
if project.startswith('a/'):
|
1933
|
+
project = project[len('a/'):]
|
1934
|
+
return project
|
1935
|
+
|
1936
|
+
def _GerritChangeIdentifier(self):
|
1937
|
+
"""Handy method for gerrit_util.ChangeIdentifier for a given CL.
|
1938
|
+
|
1939
|
+
Not to be confused by value of "Change-Id:" footer.
|
1940
|
+
If Gerrit project can be determined, this will speed up Gerrit HTTP API RPC.
|
1941
|
+
"""
|
1942
|
+
project = self._GetGerritProject()
|
1943
|
+
if project:
|
1944
|
+
return gerrit_util.ChangeIdentifier(project, self.GetIssue())
|
1945
|
+
# Fall back on still unique, but less efficient change number.
|
1946
|
+
return str(self.GetIssue())
|
1947
|
+
|
2401
1948
|
@classmethod
|
2402
1949
|
def IssueConfigKey(cls):
|
2403
1950
|
return 'gerritissue'
|
@@ -2416,13 +1963,16 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2416
1963
|
# For projects with unusual authentication schemes.
|
2417
1964
|
# See http://crbug.com/603378.
|
2418
1965
|
return
|
2419
|
-
|
2420
|
-
if
|
1966
|
+
|
1967
|
+
# Check presence of cookies only if using cookies-based auth method.
|
1968
|
+
cookie_auth = gerrit_util.Authenticator.get()
|
1969
|
+
if not isinstance(cookie_auth, gerrit_util.CookiesAuthenticator):
|
2421
1970
|
return
|
1971
|
+
|
1972
|
+
# Lazy-loader to identify Gerrit and Git hosts.
|
2422
1973
|
self.GetCodereviewServer()
|
2423
1974
|
git_host = self._GetGitHost()
|
2424
|
-
assert self._gerrit_server and self._gerrit_host
|
2425
|
-
cookie_auth = gerrit_util.CookiesAuthenticator()
|
1975
|
+
assert self._gerrit_server and self._gerrit_host and git_host
|
2426
1976
|
|
2427
1977
|
gerrit_auth = cookie_auth.get_auth_header(self._gerrit_host)
|
2428
1978
|
git_auth = cookie_auth.get_auth_header(git_host)
|
@@ -2464,7 +2014,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2464
2014
|
# Warm change details cache now to avoid RPCs later, reducing latency for
|
2465
2015
|
# developers.
|
2466
2016
|
self._GetChangeDetail(
|
2467
|
-
['DETAILED_ACCOUNTS', 'CURRENT_REVISION', 'CURRENT_COMMIT'])
|
2017
|
+
['DETAILED_ACCOUNTS', 'CURRENT_REVISION', 'CURRENT_COMMIT', 'LABELS'])
|
2468
2018
|
|
2469
2019
|
status = self._GetChangeDetail()['status']
|
2470
2020
|
if status in ('MERGED', 'ABANDONED'):
|
@@ -2472,10 +2022,15 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2472
2022
|
(self.GetIssueURL(),
|
2473
2023
|
'submitted' if status == 'MERGED' else 'abandoned'))
|
2474
2024
|
|
2475
|
-
|
2025
|
+
# TODO(vadimsh): For some reason the chunk of code below was skipped if
|
2026
|
+
# 'is_gce' is True. I'm just refactoring it to be 'skip if not cookies'.
|
2027
|
+
# Apparently this check is not very important? Otherwise get_auth_email
|
2028
|
+
# could have been added to other implementations of Authenticator.
|
2029
|
+
cookies_auth = gerrit_util.Authenticator.get()
|
2030
|
+
if not isinstance(cookies_auth, gerrit_util.CookiesAuthenticator):
|
2476
2031
|
return
|
2477
|
-
|
2478
|
-
|
2032
|
+
|
2033
|
+
cookies_user = cookies_auth.get_auth_email(self._GetGerritHost())
|
2479
2034
|
if self.GetIssueOwner() == cookies_user:
|
2480
2035
|
return
|
2481
2036
|
logging.debug('change %s owner is %s, cookies user is %s',
|
@@ -2492,7 +2047,6 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2492
2047
|
(self.GetIssue(), self.GetIssueOwner(), details['email']))
|
2493
2048
|
confirm_or_exit(action='upload')
|
2494
2049
|
|
2495
|
-
|
2496
2050
|
def _PostUnsetIssueProperties(self):
|
2497
2051
|
"""Which branch-specific properties to erase when unsetting issue."""
|
2498
2052
|
return ['gerritsquashhash']
|
@@ -2510,6 +2064,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2510
2064
|
* 'waiting' - waiting for review
|
2511
2065
|
* 'reply' - waiting for uploader to reply to review
|
2512
2066
|
* 'lgtm' - Code-Review label has been set
|
2067
|
+
* 'dry-run' - dry-running in the commit queue
|
2513
2068
|
* 'commit' - in the commit queue
|
2514
2069
|
* 'closed' - successfully submitted or abandoned
|
2515
2070
|
"""
|
@@ -2525,10 +2080,14 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2525
2080
|
if data['status'] in ('ABANDONED', 'MERGED'):
|
2526
2081
|
return 'closed'
|
2527
2082
|
|
2528
|
-
|
2529
|
-
|
2530
|
-
|
2083
|
+
cq_label = data['labels'].get('Commit-Queue', {})
|
2084
|
+
max_cq_vote = 0
|
2085
|
+
for vote in cq_label.get('all', []):
|
2086
|
+
max_cq_vote = max(max_cq_vote, vote.get('value', 0))
|
2087
|
+
if max_cq_vote == 2:
|
2531
2088
|
return 'commit'
|
2089
|
+
if max_cq_vote == 1:
|
2090
|
+
return 'dry-run'
|
2532
2091
|
|
2533
2092
|
if data['labels'].get('Code-Review', {}).get('approved'):
|
2534
2093
|
return 'lgtm'
|
@@ -2564,31 +2123,48 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2564
2123
|
data = self._GetChangeDetail(['CURRENT_REVISION', 'CURRENT_COMMIT'],
|
2565
2124
|
no_cache=force)
|
2566
2125
|
current_rev = data['current_revision']
|
2567
|
-
return data['revisions'][current_rev]['commit']['message']
|
2126
|
+
return data['revisions'][current_rev]['commit']['message'].encode(
|
2127
|
+
'utf-8', 'ignore')
|
2568
2128
|
|
2569
2129
|
def UpdateDescriptionRemote(self, description, force=False):
|
2570
|
-
if gerrit_util.HasPendingChangeEdit(
|
2130
|
+
if gerrit_util.HasPendingChangeEdit(
|
2131
|
+
self._GetGerritHost(), self._GerritChangeIdentifier()):
|
2571
2132
|
if not force:
|
2572
2133
|
confirm_or_exit(
|
2573
2134
|
'The description cannot be modified while the issue has a pending '
|
2574
2135
|
'unpublished edit. Either publish the edit in the Gerrit web UI '
|
2575
2136
|
'or delete it.\n\n', action='delete the unpublished edit')
|
2576
2137
|
|
2577
|
-
gerrit_util.DeletePendingChangeEdit(
|
2578
|
-
|
2579
|
-
gerrit_util.SetCommitMessage(
|
2580
|
-
|
2138
|
+
gerrit_util.DeletePendingChangeEdit(
|
2139
|
+
self._GetGerritHost(), self._GerritChangeIdentifier())
|
2140
|
+
gerrit_util.SetCommitMessage(
|
2141
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(),
|
2142
|
+
description, notify='NONE')
|
2581
2143
|
|
2582
2144
|
def AddComment(self, message, publish=None):
|
2583
|
-
gerrit_util.SetReview(
|
2584
|
-
|
2145
|
+
gerrit_util.SetReview(
|
2146
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(),
|
2147
|
+
msg=message, ready=publish)
|
2585
2148
|
|
2586
2149
|
def GetCommentsSummary(self, readable=True):
|
2587
2150
|
# DETAILED_ACCOUNTS is to get emails in accounts.
|
2151
|
+
# CURRENT_REVISION is included to get the latest patchset so that
|
2152
|
+
# only the robot comments from the latest patchset can be shown.
|
2588
2153
|
messages = self._GetChangeDetail(
|
2589
|
-
options=['MESSAGES', 'DETAILED_ACCOUNTS'
|
2154
|
+
options=['MESSAGES', 'DETAILED_ACCOUNTS',
|
2155
|
+
'CURRENT_REVISION']).get('messages', [])
|
2590
2156
|
file_comments = gerrit_util.GetChangeComments(
|
2591
|
-
self._GetGerritHost(), self.
|
2157
|
+
self._GetGerritHost(), self._GerritChangeIdentifier())
|
2158
|
+
robot_file_comments = gerrit_util.GetChangeRobotComments(
|
2159
|
+
self._GetGerritHost(), self._GerritChangeIdentifier())
|
2160
|
+
|
2161
|
+
# Add the robot comments onto the list of comments, but only
|
2162
|
+
# keep those that are from the latest pachset.
|
2163
|
+
latest_patch_set = self.GetMostRecentPatchset()
|
2164
|
+
for path, robot_comments in robot_file_comments.iteritems():
|
2165
|
+
line_comments = file_comments.setdefault(path, [])
|
2166
|
+
line_comments.extend(
|
2167
|
+
[c for c in robot_comments if c['patch_set'] == latest_patch_set])
|
2592
2168
|
|
2593
2169
|
# Build dictionary of file comments for easy access and sorting later.
|
2594
2170
|
# {author+date: {path: {patchset: {line: url+message}}}}
|
@@ -2596,7 +2172,8 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2596
2172
|
lambda: collections.defaultdict(lambda: collections.defaultdict(dict)))
|
2597
2173
|
for path, line_comments in file_comments.iteritems():
|
2598
2174
|
for comment in line_comments:
|
2599
|
-
|
2175
|
+
tag = comment.get('tag', '')
|
2176
|
+
if tag.startswith('autogenerated') and 'robot_id' not in comment:
|
2600
2177
|
continue
|
2601
2178
|
key = (comment['author']['email'], comment['updated'])
|
2602
2179
|
if comment.get('side', 'REVISION') == 'PARENT':
|
@@ -2610,66 +2187,76 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2610
2187
|
str(line) if line else ''))
|
2611
2188
|
comments[key][path][patchset][line] = (url, comment['message'])
|
2612
2189
|
|
2613
|
-
|
2190
|
+
summaries = []
|
2614
2191
|
for msg in messages:
|
2615
|
-
|
2616
|
-
if
|
2617
|
-
|
2618
|
-
|
2619
|
-
|
2620
|
-
|
2621
|
-
|
2622
|
-
|
2623
|
-
|
2624
|
-
|
2625
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
2638
|
-
|
2639
|
-
|
2640
|
-
|
2641
|
-
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2646
|
-
|
2647
|
-
|
2648
|
-
|
2649
|
-
|
2650
|
-
|
2651
|
-
|
2652
|
-
|
2653
|
-
|
2654
|
-
return
|
2192
|
+
summary = self._BuildCommentSummary(msg, comments, readable)
|
2193
|
+
if summary:
|
2194
|
+
summaries.append(summary)
|
2195
|
+
return summaries
|
2196
|
+
|
2197
|
+
@staticmethod
|
2198
|
+
def _BuildCommentSummary(msg, comments, readable):
|
2199
|
+
key = (msg['author']['email'], msg['date'])
|
2200
|
+
# Don't bother showing autogenerated messages that don't have associated
|
2201
|
+
# file or line comments. this will filter out most autogenerated
|
2202
|
+
# messages, but will keep robot comments like those from Tricium.
|
2203
|
+
is_autogenerated = msg.get('tag', '').startswith('autogenerated')
|
2204
|
+
if is_autogenerated and not comments.get(key):
|
2205
|
+
return None
|
2206
|
+
message = msg['message']
|
2207
|
+
# Gerrit spits out nanoseconds.
|
2208
|
+
assert len(msg['date'].split('.')[-1]) == 9
|
2209
|
+
date = datetime.datetime.strptime(msg['date'][:-3],
|
2210
|
+
'%Y-%m-%d %H:%M:%S.%f')
|
2211
|
+
if key in comments:
|
2212
|
+
message += '\n'
|
2213
|
+
for path, patchsets in sorted(comments.get(key, {}).items()):
|
2214
|
+
if readable:
|
2215
|
+
message += '\n%s' % path
|
2216
|
+
for patchset, lines in sorted(patchsets.items()):
|
2217
|
+
for line, (url, content) in sorted(lines.items()):
|
2218
|
+
if line:
|
2219
|
+
line_str = 'Line %d' % line
|
2220
|
+
path_str = '%s:%d:' % (path, line)
|
2221
|
+
else:
|
2222
|
+
line_str = 'File comment'
|
2223
|
+
path_str = '%s:0:' % path
|
2224
|
+
if readable:
|
2225
|
+
message += '\n %s, %s: %s' % (patchset, line_str, url)
|
2226
|
+
message += '\n %s\n' % content
|
2227
|
+
else:
|
2228
|
+
message += '\n%s ' % path_str
|
2229
|
+
message += '\n%s\n' % content
|
2230
|
+
|
2231
|
+
return _CommentSummary(
|
2232
|
+
date=date,
|
2233
|
+
message=message,
|
2234
|
+
sender=msg['author']['email'],
|
2235
|
+
autogenerated=is_autogenerated,
|
2236
|
+
# These could be inferred from the text messages and correlated with
|
2237
|
+
# Code-Review label maximum, however this is not reliable.
|
2238
|
+
# Leaving as is until the need arises.
|
2239
|
+
approval=False,
|
2240
|
+
disapproval=False,
|
2241
|
+
)
|
2655
2242
|
|
2656
2243
|
def CloseIssue(self):
|
2657
|
-
gerrit_util.AbandonChange(
|
2244
|
+
gerrit_util.AbandonChange(
|
2245
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(), msg='')
|
2658
2246
|
|
2659
2247
|
def SubmitIssue(self, wait_for_merge=True):
|
2660
|
-
gerrit_util.SubmitChange(
|
2661
|
-
|
2248
|
+
gerrit_util.SubmitChange(
|
2249
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(),
|
2250
|
+
wait_for_merge=wait_for_merge)
|
2662
2251
|
|
2663
|
-
def _GetChangeDetail(self, options=None,
|
2664
|
-
|
2665
|
-
"""Returns details of the issue by querying Gerrit and caching results.
|
2252
|
+
def _GetChangeDetail(self, options=None, no_cache=False):
|
2253
|
+
"""Returns details of associated Gerrit change and caching results.
|
2666
2254
|
|
2667
2255
|
If fresh data is needed, set no_cache=True which will clear cache and
|
2668
2256
|
thus new data will be fetched from Gerrit.
|
2669
2257
|
"""
|
2670
2258
|
options = options or []
|
2671
|
-
|
2672
|
-
assert issue, 'issue is required to query Gerrit'
|
2259
|
+
assert self.GetIssue(), 'issue is required to query Gerrit'
|
2673
2260
|
|
2674
2261
|
# Optimization to avoid multiple RPCs:
|
2675
2262
|
if (('CURRENT_REVISION' in options or 'ALL_REVISIONS' in options) and
|
@@ -2677,15 +2264,15 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2677
2264
|
options.append('CURRENT_COMMIT')
|
2678
2265
|
|
2679
2266
|
# Normalize issue and options for consistent keys in cache.
|
2680
|
-
|
2267
|
+
cache_key = str(self.GetIssue())
|
2681
2268
|
options = [o.upper() for o in options]
|
2682
2269
|
|
2683
2270
|
# Check in cache first unless no_cache is True.
|
2684
2271
|
if no_cache:
|
2685
|
-
self._detail_cache.pop(
|
2272
|
+
self._detail_cache.pop(cache_key, None)
|
2686
2273
|
else:
|
2687
2274
|
options_set = frozenset(options)
|
2688
|
-
for cached_options_set, data in self._detail_cache.get(
|
2275
|
+
for cached_options_set, data in self._detail_cache.get(cache_key, []):
|
2689
2276
|
# Assumption: data fetched before with extra options is suitable
|
2690
2277
|
# for return for a smaller set of options.
|
2691
2278
|
# For example, if we cached data for
|
@@ -2697,37 +2284,48 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2697
2284
|
|
2698
2285
|
try:
|
2699
2286
|
data = gerrit_util.GetChangeDetail(
|
2700
|
-
self._GetGerritHost(),
|
2287
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(), options)
|
2701
2288
|
except gerrit_util.GerritError as e:
|
2702
2289
|
if e.http_status == 404:
|
2703
|
-
raise GerritChangeNotExists(
|
2290
|
+
raise GerritChangeNotExists(self.GetIssue(), self.GetCodereviewServer())
|
2704
2291
|
raise
|
2705
2292
|
|
2706
|
-
self._detail_cache.setdefault(
|
2293
|
+
self._detail_cache.setdefault(cache_key, []).append(
|
2294
|
+
(frozenset(options), data))
|
2707
2295
|
return data
|
2708
2296
|
|
2709
|
-
def _GetChangeCommit(self
|
2710
|
-
|
2711
|
-
assert issue, 'issue is required to query Gerrit'
|
2297
|
+
def _GetChangeCommit(self):
|
2298
|
+
assert self.GetIssue(), 'issue must be set to query Gerrit'
|
2712
2299
|
try:
|
2713
|
-
data = gerrit_util.GetChangeCommit(
|
2300
|
+
data = gerrit_util.GetChangeCommit(
|
2301
|
+
self._GetGerritHost(), self._GerritChangeIdentifier())
|
2714
2302
|
except gerrit_util.GerritError as e:
|
2715
2303
|
if e.http_status == 404:
|
2716
|
-
raise GerritChangeNotExists(
|
2304
|
+
raise GerritChangeNotExists(self.GetIssue(), self.GetCodereviewServer())
|
2717
2305
|
raise
|
2718
2306
|
return data
|
2719
2307
|
|
2308
|
+
def _IsCqConfigured(self):
|
2309
|
+
detail = self._GetChangeDetail(['LABELS'])
|
2310
|
+
if not u'Commit-Queue' in detail.get('labels', {}):
|
2311
|
+
return False
|
2312
|
+
# TODO(crbug/753213): Remove temporary hack
|
2313
|
+
if ('https://chromium.googlesource.com/chromium/src' ==
|
2314
|
+
self._changelist.GetRemoteUrl() and
|
2315
|
+
detail['branch'].startswith('refs/branch-heads/')):
|
2316
|
+
return False
|
2317
|
+
return True
|
2318
|
+
|
2720
2319
|
def CMDLand(self, force, bypass_hooks, verbose, parallel):
|
2721
2320
|
if git_common.is_dirty_git_tree('land'):
|
2722
2321
|
return 1
|
2322
|
+
|
2723
2323
|
detail = self._GetChangeDetail(['CURRENT_REVISION', 'LABELS'])
|
2724
|
-
if
|
2725
|
-
|
2726
|
-
confirm_or_exit('\nIt seems this repository has a Commit Queue, '
|
2324
|
+
if not force and self._IsCqConfigured():
|
2325
|
+
confirm_or_exit('\nIt seems this repository has a Commit Queue, '
|
2727
2326
|
'which can test and land changes for you. '
|
2728
2327
|
'Are you sure you wish to bypass it?\n',
|
2729
2328
|
action='bypass CQ')
|
2730
|
-
|
2731
2329
|
differs = True
|
2732
2330
|
last_upload = self._GitGetBranchConfigValue('gerritsquashhash')
|
2733
2331
|
# Note: git diff outputs nothing if there is no diff.
|
@@ -2797,7 +2395,10 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2797
2395
|
remote_url = self._changelist.GetRemoteUrl()
|
2798
2396
|
if remote_url.endswith('.git'):
|
2799
2397
|
remote_url = remote_url[:-len('.git')]
|
2398
|
+
remote_url = remote_url.rstrip('/')
|
2399
|
+
|
2800
2400
|
fetch_info = revision_info['fetch']['http']
|
2401
|
+
fetch_info['url'] = fetch_info['url'].rstrip('/')
|
2801
2402
|
|
2802
2403
|
if remote_url != fetch_info['url']:
|
2803
2404
|
DieWithError('Trying to patch a change from %s but this repo appears '
|
@@ -2890,7 +2491,6 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2890
2491
|
|
2891
2492
|
remote, remote_branch = self.GetRemoteBranch()
|
2892
2493
|
branch = GetTargetRef(remote, remote_branch, options.target_branch)
|
2893
|
-
|
2894
2494
|
# This may be None; default fallback value is determined in logic below.
|
2895
2495
|
title = options.title
|
2896
2496
|
|
@@ -2961,7 +2561,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
2961
2561
|
if options.message:
|
2962
2562
|
message = options.message
|
2963
2563
|
else:
|
2964
|
-
message =
|
2564
|
+
message = _create_description_from_log(git_diff_args)
|
2965
2565
|
if options.title:
|
2966
2566
|
message = options.title + '\n\n' + message
|
2967
2567
|
change_desc = ChangeDescription(message)
|
@@ -3001,7 +2601,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3001
2601
|
os.remove(desc_tempfile.name)
|
3002
2602
|
else:
|
3003
2603
|
change_desc = ChangeDescription(
|
3004
|
-
options.message or
|
2604
|
+
options.message or _create_description_from_log(git_diff_args))
|
3005
2605
|
if not change_desc.description:
|
3006
2606
|
DieWithError("Description is empty. Aborting...")
|
3007
2607
|
|
@@ -3020,6 +2620,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3020
2620
|
change_id = git_footers.get_footer_change_id(change_desc.description)[0]
|
3021
2621
|
|
3022
2622
|
assert change_desc
|
2623
|
+
SaveDescriptionBackup(change_desc)
|
3023
2624
|
commits = RunGitSilent(['rev-list', '%s..%s' % (parent,
|
3024
2625
|
ref_to_push)]).splitlines()
|
3025
2626
|
if len(commits) > 1:
|
@@ -3034,6 +2635,27 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3034
2635
|
change_desc.update_reviewers(options.reviewers, options.tbrs,
|
3035
2636
|
options.add_owners_to, change)
|
3036
2637
|
|
2638
|
+
reviewers = sorted(change_desc.get_reviewers())
|
2639
|
+
# Add cc's from the CC_LIST and --cc flag (if any).
|
2640
|
+
if not options.private and not options.no_autocc:
|
2641
|
+
cc = self.GetCCList().split(',')
|
2642
|
+
else:
|
2643
|
+
cc = []
|
2644
|
+
if options.cc:
|
2645
|
+
cc.extend(options.cc)
|
2646
|
+
cc = filter(None, [email.strip() for email in cc])
|
2647
|
+
if change_desc.get_cced():
|
2648
|
+
cc.extend(change_desc.get_cced())
|
2649
|
+
if self._GetGerritHost() == 'chromium-review.googlesource.com':
|
2650
|
+
valid_accounts = set(reviewers + cc)
|
2651
|
+
# TODO(crbug/877717): relax this for all hosts.
|
2652
|
+
else:
|
2653
|
+
valid_accounts = gerrit_util.ValidAccounts(
|
2654
|
+
self._GetGerritHost(), reviewers + cc)
|
2655
|
+
logging.info('accounts %s are recognized, %s invalid',
|
2656
|
+
sorted(valid_accounts),
|
2657
|
+
set(reviewers + cc).difference(set(valid_accounts)))
|
2658
|
+
|
3037
2659
|
# Extra options that can be specified at push time. Doc:
|
3038
2660
|
# https://gerrit-review.googlesource.com/Documentation/user-upload.html
|
3039
2661
|
refspec_opts = []
|
@@ -3059,11 +2681,39 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3059
2681
|
if options.private:
|
3060
2682
|
refspec_opts.append('private')
|
3061
2683
|
|
2684
|
+
for r in sorted(reviewers):
|
2685
|
+
if r in valid_accounts:
|
2686
|
+
refspec_opts.append('r=%s' % r)
|
2687
|
+
reviewers.remove(r)
|
2688
|
+
else:
|
2689
|
+
# TODO(tandrii): this should probably be a hard failure.
|
2690
|
+
print('WARNING: reviewer %s doesn\'t have a Gerrit account, skipping'
|
2691
|
+
% r)
|
2692
|
+
for c in sorted(cc):
|
2693
|
+
# refspec option will be rejected if cc doesn't correspond to an
|
2694
|
+
# account, even though REST call to add such arbitrary cc may succeed.
|
2695
|
+
if c in valid_accounts:
|
2696
|
+
refspec_opts.append('cc=%s' % c)
|
2697
|
+
cc.remove(c)
|
2698
|
+
|
3062
2699
|
if options.topic:
|
3063
2700
|
# Documentation on Gerrit topics is here:
|
3064
2701
|
# https://gerrit-review.googlesource.com/Documentation/user-upload.html#topic
|
3065
2702
|
refspec_opts.append('topic=%s' % options.topic)
|
3066
2703
|
|
2704
|
+
if options.enable_auto_submit:
|
2705
|
+
refspec_opts.append('l=Auto-Submit+1')
|
2706
|
+
if options.use_commit_queue:
|
2707
|
+
refspec_opts.append('l=Commit-Queue+2')
|
2708
|
+
elif options.cq_dry_run:
|
2709
|
+
refspec_opts.append('l=Commit-Queue+1')
|
2710
|
+
|
2711
|
+
if change_desc.get_reviewers(tbr_only=True):
|
2712
|
+
score = gerrit_util.GetCodeReviewTbrScore(
|
2713
|
+
self._GetGerritHost(),
|
2714
|
+
self._GetGerritProject())
|
2715
|
+
refspec_opts.append('l=Code-Review+%s' % score)
|
2716
|
+
|
3067
2717
|
# Gerrit sorts hashtags, so order is not important.
|
3068
2718
|
hashtags = {change_desc.sanitize_hash_tag(t) for t in options.hashtags}
|
3069
2719
|
if not self.GetIssue():
|
@@ -3078,19 +2728,29 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3078
2728
|
refspec = '%s:refs/for/%s%s' % (ref_to_push, branch, refspec_suffix)
|
3079
2729
|
|
3080
2730
|
try:
|
2731
|
+
push_returncode = 0
|
2732
|
+
before_push = time_time()
|
3081
2733
|
push_stdout = gclient_utils.CheckCallAndFilter(
|
3082
2734
|
['git', 'push', self.GetRemoteUrl(), refspec],
|
3083
2735
|
print_stdout=True,
|
3084
2736
|
# Flush after every line: useful for seeing progress when running as
|
3085
2737
|
# recipe.
|
3086
2738
|
filter_fn=lambda _: sys.stdout.flush())
|
3087
|
-
except subprocess2.CalledProcessError:
|
2739
|
+
except subprocess2.CalledProcessError as e:
|
2740
|
+
push_returncode = e.returncode
|
3088
2741
|
DieWithError('Failed to create a change. Please examine output above '
|
3089
2742
|
'for the reason of the failure.\n'
|
3090
2743
|
'Hint: run command below to diagnose common Git/Gerrit '
|
3091
2744
|
'credential problems:\n'
|
3092
2745
|
' git cl creds-check\n',
|
3093
2746
|
change_desc)
|
2747
|
+
finally:
|
2748
|
+
metrics.collector.add_repeated('sub_commands', {
|
2749
|
+
'command': 'git push',
|
2750
|
+
'execution_time': time_time() - before_push,
|
2751
|
+
'exit_code': push_returncode,
|
2752
|
+
'arguments': metrics_utils.extract_known_subcommand_args(refspec_opts),
|
2753
|
+
})
|
3094
2754
|
|
3095
2755
|
if options.squash:
|
3096
2756
|
regex = re.compile(r'remote:\s+https?://[\w\-\.\+\/#]*/(\d+)\s.*')
|
@@ -3104,33 +2764,14 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3104
2764
|
self.SetIssue(change_numbers[0])
|
3105
2765
|
self._GitSetBranchConfigValue('gerritsquashhash', ref_to_push)
|
3106
2766
|
|
3107
|
-
|
3108
|
-
|
3109
|
-
|
3110
|
-
|
3111
|
-
|
3112
|
-
|
3113
|
-
|
3114
|
-
|
3115
|
-
cc.extend(options.cc)
|
3116
|
-
cc = filter(None, [email.strip() for email in cc])
|
3117
|
-
if change_desc.get_cced():
|
3118
|
-
cc.extend(change_desc.get_cced())
|
3119
|
-
|
3120
|
-
gerrit_util.AddReviewers(
|
3121
|
-
self._GetGerritHost(), self.GetIssue(), reviewers, cc,
|
3122
|
-
notify=bool(options.send_mail))
|
3123
|
-
|
3124
|
-
if change_desc.get_reviewers(tbr_only=True):
|
3125
|
-
labels = self._GetChangeDetail(['LABELS']).get('labels', {})
|
3126
|
-
score = 1
|
3127
|
-
if 'Code-Review' in labels and 'values' in labels['Code-Review']:
|
3128
|
-
score = max([int(x) for x in labels['Code-Review']['values'].keys()])
|
3129
|
-
print('Adding self-LGTM (Code-Review +%d) because of TBRs.' % score)
|
3130
|
-
gerrit_util.SetReview(
|
3131
|
-
self._GetGerritHost(), self.GetIssue(),
|
3132
|
-
msg='Self-approving for TBR',
|
3133
|
-
labels={'Code-Review': score})
|
2767
|
+
if self.GetIssue() and (reviewers or cc):
|
2768
|
+
# GetIssue() is not set in case of non-squash uploads according to tests.
|
2769
|
+
# TODO(agable): non-squash uploads in git cl should be removed.
|
2770
|
+
gerrit_util.AddReviewers(
|
2771
|
+
self._GetGerritHost(),
|
2772
|
+
self._GerritChangeIdentifier(),
|
2773
|
+
reviewers, cc,
|
2774
|
+
notify=bool(options.send_mail))
|
3134
2775
|
|
3135
2776
|
return 0
|
3136
2777
|
|
@@ -3198,31 +2839,16 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3198
2839
|
"""Re-commits using the current message, assumes the commit hook is in
|
3199
2840
|
place.
|
3200
2841
|
"""
|
3201
|
-
log_desc = options.message or
|
2842
|
+
log_desc = options.message or _create_description_from_log(args)
|
3202
2843
|
git_command = ['commit', '--amend', '-m', log_desc]
|
3203
2844
|
RunGit(git_command)
|
3204
|
-
new_log_desc =
|
2845
|
+
new_log_desc = _create_description_from_log(args)
|
3205
2846
|
if git_footers.get_footer_change_id(new_log_desc):
|
3206
2847
|
print('git-cl: Added Change-Id to commit message.')
|
3207
2848
|
return new_log_desc
|
3208
2849
|
else:
|
3209
2850
|
DieWithError('ERROR: Gerrit commit-msg hook not installed.')
|
3210
2851
|
|
3211
|
-
def SetLabels(self, enable_auto_submit, use_commit_queue, cq_dry_run):
|
3212
|
-
"""Sets labels on the change based on the provided flags."""
|
3213
|
-
labels = {}
|
3214
|
-
notify = None;
|
3215
|
-
if enable_auto_submit:
|
3216
|
-
labels['Auto-Submit'] = 1
|
3217
|
-
if use_commit_queue:
|
3218
|
-
labels['Commit-Queue'] = 2
|
3219
|
-
elif cq_dry_run:
|
3220
|
-
labels['Commit-Queue'] = 1
|
3221
|
-
notify = False
|
3222
|
-
if labels:
|
3223
|
-
gerrit_util.SetReview(self._GetGerritHost(), self.GetIssue(),
|
3224
|
-
labels=labels, notify=notify)
|
3225
|
-
|
3226
2852
|
def SetCQState(self, new_state):
|
3227
2853
|
"""Sets the Commit-Queue label assuming canonical CQ config for Gerrit."""
|
3228
2854
|
vote_map = {
|
@@ -3232,8 +2858,9 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3232
2858
|
}
|
3233
2859
|
labels = {'Commit-Queue': vote_map[new_state]}
|
3234
2860
|
notify = False if new_state == _CQState.DRY_RUN else None
|
3235
|
-
gerrit_util.SetReview(
|
3236
|
-
|
2861
|
+
gerrit_util.SetReview(
|
2862
|
+
self._GetGerritHost(), self._GerritChangeIdentifier(),
|
2863
|
+
labels=labels, notify=notify)
|
3237
2864
|
|
3238
2865
|
def CannotTriggerTryJobReason(self):
|
3239
2866
|
try:
|
@@ -3271,11 +2898,10 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
|
|
3271
2898
|
|
3272
2899
|
def GetReviewers(self):
|
3273
2900
|
details = self._GetChangeDetail(['DETAILED_ACCOUNTS'])
|
3274
|
-
return [
|
2901
|
+
return [r['email'] for r in details['reviewers'].get('REVIEWER', [])]
|
3275
2902
|
|
3276
2903
|
|
3277
2904
|
_CODEREVIEW_IMPLEMENTATIONS = {
|
3278
|
-
'rietveld': _RietveldChangelistImpl,
|
3279
2905
|
'gerrit': _GerritChangelistImpl,
|
3280
2906
|
}
|
3281
2907
|
|
@@ -3310,13 +2936,11 @@ def _add_codereview_select_options(parser):
|
|
3310
2936
|
|
3311
2937
|
|
3312
2938
|
def _process_codereview_select_options(parser, options):
|
3313
|
-
if options.
|
3314
|
-
parser.error('
|
2939
|
+
if options.rietveld:
|
2940
|
+
parser.error('--rietveld is no longer supported')
|
3315
2941
|
options.forced_codereview = None
|
3316
2942
|
if options.gerrit:
|
3317
2943
|
options.forced_codereview = 'gerrit'
|
3318
|
-
elif options.rietveld:
|
3319
|
-
options.forced_codereview = 'rietveld'
|
3320
2944
|
|
3321
2945
|
|
3322
2946
|
def _get_bug_line_values(default_project, bugs):
|
@@ -3464,8 +3088,8 @@ class ChangeDescription(object):
|
|
3464
3088
|
] + self._description_lines)
|
3465
3089
|
|
3466
3090
|
regexp = re.compile(self.BUG_LINE)
|
3091
|
+
prefix = settings.GetBugPrefix()
|
3467
3092
|
if not any((regexp.match(line) for line in self._description_lines)):
|
3468
|
-
prefix = settings.GetBugPrefix()
|
3469
3093
|
values = list(_get_bug_line_values(prefix, bug or '')) or [prefix]
|
3470
3094
|
if git_footer:
|
3471
3095
|
self.append_footer('Bug: %s' % ', '.join(values))
|
@@ -3481,7 +3105,9 @@ class ChangeDescription(object):
|
|
3481
3105
|
|
3482
3106
|
# Strip off comments and default inserted "Bug:" line.
|
3483
3107
|
clean_lines = [line.rstrip() for line in lines if not
|
3484
|
-
(line.startswith('#') or
|
3108
|
+
(line.startswith('#') or
|
3109
|
+
line.rstrip() == "Bug:" or
|
3110
|
+
line.rstrip() == "Bug: " + prefix)]
|
3485
3111
|
if not clean_lines:
|
3486
3112
|
DieWithError('No CL description, aborting')
|
3487
3113
|
self.set_description(clean_lines)
|
@@ -3675,13 +3301,11 @@ def LoadCodereviewSettingsFromFile(fileobj):
|
|
3675
3301
|
# Only server setting is required. Other settings can be absent.
|
3676
3302
|
# In that case, we ignore errors raised during option deletion attempt.
|
3677
3303
|
SetProperty('cc', 'CC_LIST', unset_error_ok=True)
|
3678
|
-
SetProperty('private', 'PRIVATE', unset_error_ok=True)
|
3679
3304
|
SetProperty('tree-status-url', 'STATUS', unset_error_ok=True)
|
3680
3305
|
SetProperty('viewvc-url', 'VIEW_VC', unset_error_ok=True)
|
3681
3306
|
SetProperty('bug-prefix', 'BUG_PREFIX', unset_error_ok=True)
|
3682
3307
|
SetProperty('cpplint-regex', 'LINT_REGEX', unset_error_ok=True)
|
3683
3308
|
SetProperty('cpplint-ignore-regex', 'LINT_IGNORE_REGEX', unset_error_ok=True)
|
3684
|
-
SetProperty('project', 'PROJECT', unset_error_ok=True)
|
3685
3309
|
SetProperty('run-post-upload-hook', 'RUN_POST_UPLOAD_HOOK',
|
3686
3310
|
unset_error_ok=True)
|
3687
3311
|
|
@@ -3753,43 +3377,6 @@ def DownloadGerritHook(force):
|
|
3753
3377
|
'chmod +x .git/hooks/commit-msg' % src)
|
3754
3378
|
|
3755
3379
|
|
3756
|
-
def GetRietveldCodereviewSettingsInteractively():
|
3757
|
-
"""Prompt the user for settings."""
|
3758
|
-
server = settings.GetDefaultServerUrl(error_ok=True)
|
3759
|
-
prompt = 'Rietveld server (host[:port])'
|
3760
|
-
prompt += ' [%s]' % (server or DEFAULT_SERVER)
|
3761
|
-
newserver = ask_for_data(prompt + ':')
|
3762
|
-
if not server and not newserver:
|
3763
|
-
newserver = DEFAULT_SERVER
|
3764
|
-
if newserver:
|
3765
|
-
newserver = gclient_utils.UpgradeToHttps(newserver)
|
3766
|
-
if newserver != server:
|
3767
|
-
RunGit(['config', 'rietveld.server', newserver])
|
3768
|
-
|
3769
|
-
def SetProperty(initial, caption, name, is_url):
|
3770
|
-
prompt = caption
|
3771
|
-
if initial:
|
3772
|
-
prompt += ' ("x" to clear) [%s]' % initial
|
3773
|
-
new_val = ask_for_data(prompt + ':')
|
3774
|
-
if new_val == 'x':
|
3775
|
-
RunGit(['config', '--unset-all', 'rietveld.' + name], error_ok=True)
|
3776
|
-
elif new_val:
|
3777
|
-
if is_url:
|
3778
|
-
new_val = gclient_utils.UpgradeToHttps(new_val)
|
3779
|
-
if new_val != initial:
|
3780
|
-
RunGit(['config', 'rietveld.' + name, new_val])
|
3781
|
-
|
3782
|
-
SetProperty(settings.GetDefaultCCList(), 'CC list', 'cc', False)
|
3783
|
-
SetProperty(settings.GetDefaultPrivateFlag(),
|
3784
|
-
'Private flag (rietveld only)', 'private', False)
|
3785
|
-
SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL',
|
3786
|
-
'tree-status-url', False)
|
3787
|
-
SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url', True)
|
3788
|
-
SetProperty(settings.GetBugPrefix(), 'Bug Prefix', 'bug-prefix', False)
|
3789
|
-
SetProperty(settings.GetRunPostUploadHook(), 'Run Post Upload Hook',
|
3790
|
-
'run-post-upload-hook', False)
|
3791
|
-
|
3792
|
-
|
3793
3380
|
class _GitCookiesChecker(object):
|
3794
3381
|
"""Provides facilities for validating and suggesting fixes to .gitcookies."""
|
3795
3382
|
|
@@ -4068,14 +3655,22 @@ class _GitCookiesChecker(object):
|
|
4068
3655
|
return found
|
4069
3656
|
|
4070
3657
|
|
3658
|
+
@metrics.collector.collect_metrics('git cl creds-check')
|
4071
3659
|
def CMDcreds_check(parser, args):
|
4072
3660
|
"""Checks credentials and suggests changes."""
|
4073
3661
|
_, _ = parser.parse_args(args)
|
4074
3662
|
|
4075
|
-
if
|
3663
|
+
# Code below checks .gitcookies. Abort if using something else.
|
3664
|
+
authn = gerrit_util.Authenticator.get()
|
3665
|
+
if not isinstance(authn, gerrit_util.CookiesAuthenticator):
|
3666
|
+
if isinstance(authn, gerrit_util.GceAuthenticator):
|
3667
|
+
DieWithError(
|
3668
|
+
'This command is not designed for GCE, are you on a bot?\n'
|
3669
|
+
'If you need to run this on GCE, export SKIP_GCE_AUTH_FOR_GIT=1 '
|
3670
|
+
'in your env.')
|
4076
3671
|
DieWithError(
|
4077
|
-
'This command is not designed for
|
4078
|
-
'
|
3672
|
+
'This command is not designed for bot environment. It checks '
|
3673
|
+
'~/.gitcookies file not generally used on bots.')
|
4079
3674
|
|
4080
3675
|
checker = _GitCookiesChecker()
|
4081
3676
|
checker.ensure_configured_gitcookies()
|
@@ -4089,42 +3684,7 @@ def CMDcreds_check(parser, args):
|
|
4089
3684
|
return 1
|
4090
3685
|
|
4091
3686
|
|
4092
|
-
@
|
4093
|
-
def CMDconfig(parser, args):
|
4094
|
-
"""Edits configuration for this tree."""
|
4095
|
-
|
4096
|
-
print('WARNING: git cl config works for Rietveld only.')
|
4097
|
-
# TODO(tandrii): remove this once we switch to Gerrit.
|
4098
|
-
# See bugs http://crbug.com/637561 and http://crbug.com/600469.
|
4099
|
-
parser.add_option('--activate-update', action='store_true',
|
4100
|
-
help='activate auto-updating [rietveld] section in '
|
4101
|
-
'.git/config')
|
4102
|
-
parser.add_option('--deactivate-update', action='store_true',
|
4103
|
-
help='deactivate auto-updating [rietveld] section in '
|
4104
|
-
'.git/config')
|
4105
|
-
options, args = parser.parse_args(args)
|
4106
|
-
|
4107
|
-
if options.deactivate_update:
|
4108
|
-
RunGit(['config', 'rietveld.autoupdate', 'false'])
|
4109
|
-
return
|
4110
|
-
|
4111
|
-
if options.activate_update:
|
4112
|
-
RunGit(['config', '--unset', 'rietveld.autoupdate'])
|
4113
|
-
return
|
4114
|
-
|
4115
|
-
if len(args) == 0:
|
4116
|
-
GetRietveldCodereviewSettingsInteractively()
|
4117
|
-
return 0
|
4118
|
-
|
4119
|
-
url = args[0]
|
4120
|
-
if not url.endswith('codereview.settings'):
|
4121
|
-
url = os.path.join(url, 'codereview.settings')
|
4122
|
-
|
4123
|
-
# Load code review settings and download hooks (if available).
|
4124
|
-
LoadCodereviewSettingsFromFile(urllib2.urlopen(url))
|
4125
|
-
return 0
|
4126
|
-
|
4127
|
-
|
3687
|
+
@metrics.collector.collect_metrics('git cl baseurl')
|
4128
3688
|
def CMDbaseurl(parser, args):
|
4129
3689
|
"""Gets or sets base-url for this branch."""
|
4130
3690
|
branchref = RunGit(['symbolic-ref', 'HEAD']).strip()
|
@@ -4139,7 +3699,6 @@ def CMDbaseurl(parser, args):
|
|
4139
3699
|
return RunGit(['config', 'branch.%s.base-url' % branch, args[0]],
|
4140
3700
|
error_ok=False).strip()
|
4141
3701
|
|
4142
|
-
|
4143
3702
|
def color_for_status(status):
|
4144
3703
|
"""Maps a Changelist status to color, for CMDstatus and other tools."""
|
4145
3704
|
return {
|
@@ -4166,9 +3725,6 @@ def get_cl_statuses(changes, fine_grained, max_processes=None):
|
|
4166
3725
|
|
4167
3726
|
See GetStatus() for a list of possible statuses.
|
4168
3727
|
"""
|
4169
|
-
# Silence upload.py otherwise it becomes unwieldy.
|
4170
|
-
upload.verbosity = 0
|
4171
|
-
|
4172
3728
|
if not changes:
|
4173
3729
|
raise StopIteration()
|
4174
3730
|
|
@@ -4240,7 +3796,7 @@ def upload_branch_deps(cl, args):
|
|
4240
3796
|
if root_branch is None:
|
4241
3797
|
DieWithError('Can\'t find dependent branches from detached HEAD state. '
|
4242
3798
|
'Get on a branch!')
|
4243
|
-
if not cl.GetIssue()
|
3799
|
+
if not cl.GetIssue():
|
4244
3800
|
DieWithError('Current branch does not have an uploaded CL. We cannot set '
|
4245
3801
|
'patchset dependencies without an uploaded CL.')
|
4246
3802
|
|
@@ -4280,10 +3836,6 @@ def upload_branch_deps(cl, args):
|
|
4280
3836
|
confirm_or_exit('This command will checkout all dependent branches and run '
|
4281
3837
|
'"git cl upload".', action='continue')
|
4282
3838
|
|
4283
|
-
# Add a default patchset title to all upload calls in Rietveld.
|
4284
|
-
if not cl.IsGerrit():
|
4285
|
-
args.extend(['-t', 'Updated patchset dependency'])
|
4286
|
-
|
4287
3839
|
# Record all dependents that failed to upload.
|
4288
3840
|
failures = {}
|
4289
3841
|
# Go through all dependents, checkout the branch and upload.
|
@@ -4315,6 +3867,7 @@ def upload_branch_deps(cl, args):
|
|
4315
3867
|
return 0
|
4316
3868
|
|
4317
3869
|
|
3870
|
+
@metrics.collector.collect_metrics('git cl archive')
|
4318
3871
|
def CMDarchive(parser, args):
|
4319
3872
|
"""Archives and deletes branches associated with closed changelists."""
|
4320
3873
|
parser.add_option(
|
@@ -4351,7 +3904,7 @@ def CMDarchive(parser, args):
|
|
4351
3904
|
proposal = [(cl.GetBranch(),
|
4352
3905
|
'git-cl-archived-%s-%s' % (cl.GetIssue(), cl.GetBranch()))
|
4353
3906
|
for cl, status in statuses
|
4354
|
-
if status
|
3907
|
+
if status in ('closed', 'rietveld-not-supported')]
|
4355
3908
|
proposal.sort()
|
4356
3909
|
|
4357
3910
|
if not proposal:
|
@@ -4396,6 +3949,7 @@ def CMDarchive(parser, args):
|
|
4396
3949
|
return 0
|
4397
3950
|
|
4398
3951
|
|
3952
|
+
@metrics.collector.collect_metrics('git cl status')
|
4399
3953
|
def CMDstatus(parser, args):
|
4400
3954
|
"""Show status of changelists.
|
4401
3955
|
|
@@ -4410,6 +3964,10 @@ def CMDstatus(parser, args):
|
|
4410
3964
|
|
4411
3965
|
Also see 'git cl comments'.
|
4412
3966
|
"""
|
3967
|
+
parser.add_option(
|
3968
|
+
'--no-branch-color',
|
3969
|
+
action='store_true',
|
3970
|
+
help='Disable colorized branch names')
|
4413
3971
|
parser.add_option('--field',
|
4414
3972
|
help='print only specific field (desc|id|patch|status|url)')
|
4415
3973
|
parser.add_option('-f', '--fast', action='store_true',
|
@@ -4464,8 +4022,26 @@ def CMDstatus(parser, args):
|
|
4464
4022
|
fine_grained=not options.fast,
|
4465
4023
|
max_processes=options.maxjobs)
|
4466
4024
|
|
4025
|
+
current_branch = GetCurrentBranch()
|
4026
|
+
|
4027
|
+
def FormatBranchName(branch, colorize=False):
|
4028
|
+
"""Simulates 'git branch' behavior. Colorizes and prefixes branch name with
|
4029
|
+
an asterisk when it is the current branch."""
|
4030
|
+
|
4031
|
+
asterisk = ""
|
4032
|
+
color = Fore.RESET
|
4033
|
+
if branch == current_branch:
|
4034
|
+
asterisk = "* "
|
4035
|
+
color = Fore.GREEN
|
4036
|
+
branch_name = ShortBranchName(branch)
|
4037
|
+
|
4038
|
+
if colorize:
|
4039
|
+
return asterisk + color + branch_name + Fore.RESET
|
4040
|
+
return asterisk + branch_name
|
4041
|
+
|
4467
4042
|
branch_statuses = {}
|
4468
|
-
|
4043
|
+
|
4044
|
+
alignment = max(5, max(len(FormatBranchName(c.GetBranch())) for c in changes))
|
4469
4045
|
for cl in sorted(changes, key=lambda c: c.GetBranch()):
|
4470
4046
|
branch = cl.GetBranch()
|
4471
4047
|
while branch not in branch_statuses:
|
@@ -4483,16 +4059,19 @@ def CMDstatus(parser, args):
|
|
4483
4059
|
color = ''
|
4484
4060
|
reset = ''
|
4485
4061
|
status_str = '(%s)' % status if status else ''
|
4486
|
-
print(' %*s : %s%s %s%s' % (
|
4487
|
-
alignment, ShortBranchName(branch), color, url,
|
4488
|
-
status_str, reset))
|
4489
4062
|
|
4063
|
+
branch_display = FormatBranchName(branch)
|
4064
|
+
padding = ' ' * (alignment - len(branch_display))
|
4065
|
+
if not options.no_branch_color:
|
4066
|
+
branch_display = FormatBranchName(branch, colorize=True)
|
4067
|
+
|
4068
|
+
print(' %s : %s%s %s%s' % (padding + branch_display, color, url,
|
4069
|
+
status_str, reset))
|
4490
4070
|
|
4491
|
-
branch = GetCurrentBranch()
|
4492
4071
|
print()
|
4493
|
-
print('Current branch: %s' %
|
4072
|
+
print('Current branch: %s' % current_branch)
|
4494
4073
|
for cl in changes:
|
4495
|
-
if cl.GetBranch() ==
|
4074
|
+
if cl.GetBranch() == current_branch:
|
4496
4075
|
break
|
4497
4076
|
if not cl.GetIssue():
|
4498
4077
|
print('No issue assigned.')
|
@@ -4529,6 +4108,7 @@ def write_json(path, contents):
|
|
4529
4108
|
|
4530
4109
|
|
4531
4110
|
@subcommand.usage('[issue_number]')
|
4111
|
+
@metrics.collector.collect_metrics('git cl issue')
|
4532
4112
|
def CMDissue(parser, args):
|
4533
4113
|
"""Sets or displays the current code review issue number.
|
4534
4114
|
|
@@ -4549,18 +4129,32 @@ def CMDissue(parser, args):
|
|
4549
4129
|
'--format=%(refname)']).splitlines()
|
4550
4130
|
# Reverse issue lookup.
|
4551
4131
|
issue_branch_map = {}
|
4132
|
+
|
4133
|
+
git_config = {}
|
4134
|
+
for config in RunGit(['config', '--get-regexp',
|
4135
|
+
r'branch\..*issue']).splitlines():
|
4136
|
+
name, _space, val = config.partition(' ')
|
4137
|
+
git_config[name] = val
|
4138
|
+
|
4552
4139
|
for branch in branches:
|
4553
|
-
|
4554
|
-
|
4140
|
+
for cls in _CODEREVIEW_IMPLEMENTATIONS.values():
|
4141
|
+
config_key = _git_branch_config_key(ShortBranchName(branch),
|
4142
|
+
cls.IssueConfigKey())
|
4143
|
+
issue = git_config.get(config_key)
|
4144
|
+
if issue:
|
4145
|
+
issue_branch_map.setdefault(int(issue), []).append(branch)
|
4555
4146
|
if not args:
|
4556
4147
|
args = sorted(issue_branch_map.iterkeys())
|
4557
4148
|
result = {}
|
4558
4149
|
for issue in args:
|
4559
|
-
|
4150
|
+
try:
|
4151
|
+
issue_num = int(issue)
|
4152
|
+
except ValueError:
|
4153
|
+
print('ERROR cannot parse issue number: %s' % issue, file=sys.stderr)
|
4560
4154
|
continue
|
4561
|
-
result[
|
4155
|
+
result[issue_num] = issue_branch_map.get(issue_num)
|
4562
4156
|
print('Branch for issue number %s: %s' % (
|
4563
|
-
issue, ', '.join(issue_branch_map.get(
|
4157
|
+
issue, ', '.join(issue_branch_map.get(issue_num) or ('None',))))
|
4564
4158
|
if options.json:
|
4565
4159
|
write_json(options.json, result)
|
4566
4160
|
return 0
|
@@ -4584,10 +4178,13 @@ def CMDissue(parser, args):
|
|
4584
4178
|
return 0
|
4585
4179
|
|
4586
4180
|
|
4181
|
+
@metrics.collector.collect_metrics('git cl comments')
|
4587
4182
|
def CMDcomments(parser, args):
|
4588
4183
|
"""Shows or posts review comments for any changelist."""
|
4589
4184
|
parser.add_option('-a', '--add-comment', dest='comment',
|
4590
4185
|
help='comment to add to an issue')
|
4186
|
+
parser.add_option('-p', '--publish', action='store_true',
|
4187
|
+
help='marks CL as ready and sends comment to reviewers')
|
4591
4188
|
parser.add_option('-i', '--issue', dest='issue',
|
4592
4189
|
help='review issue id (defaults to current issue). '
|
4593
4190
|
'If given, requires --rietveld or --gerrit')
|
@@ -4609,15 +4206,14 @@ def CMDcomments(parser, args):
|
|
4609
4206
|
issue = int(options.issue)
|
4610
4207
|
except ValueError:
|
4611
4208
|
DieWithError('A review issue id is expected to be a number')
|
4612
|
-
if not options.forced_codereview:
|
4613
|
-
parser.error('--gerrit or --rietveld is required if --issue is specified')
|
4614
4209
|
|
4615
|
-
cl = Changelist(issue=issue,
|
4616
|
-
|
4617
|
-
|
4210
|
+
cl = Changelist(issue=issue, codereview='gerrit', auth_config=auth_config)
|
4211
|
+
|
4212
|
+
if not cl.IsGerrit():
|
4213
|
+
parser.error('rietveld is not supported')
|
4618
4214
|
|
4619
4215
|
if options.comment:
|
4620
|
-
cl.AddComment(options.comment)
|
4216
|
+
cl.AddComment(options.comment, options.publish)
|
4621
4217
|
return 0
|
4622
4218
|
|
4623
4219
|
summary = sorted(cl.GetCommentsSummary(readable=options.readable),
|
@@ -4629,6 +4225,8 @@ def CMDcomments(parser, args):
|
|
4629
4225
|
color = Fore.GREEN
|
4630
4226
|
elif comment.sender == cl.GetIssueOwner():
|
4631
4227
|
color = Fore.MAGENTA
|
4228
|
+
elif comment.autogenerated:
|
4229
|
+
color = Fore.CYAN
|
4632
4230
|
else:
|
4633
4231
|
color = Fore.BLUE
|
4634
4232
|
print('\n%s%s %s%s\n%s' % (
|
@@ -4643,12 +4241,12 @@ def CMDcomments(parser, args):
|
|
4643
4241
|
dct = c.__dict__.copy()
|
4644
4242
|
dct['date'] = dct['date'].strftime('%Y-%m-%d %H:%M:%S.%f')
|
4645
4243
|
return dct
|
4646
|
-
|
4647
|
-
json.dump(map(pre_serialize, summary), f)
|
4244
|
+
write_json(options.json_file, map(pre_serialize, summary))
|
4648
4245
|
return 0
|
4649
4246
|
|
4650
4247
|
|
4651
4248
|
@subcommand.usage('[codereview url or issue id]')
|
4249
|
+
@metrics.collector.collect_metrics('git cl description')
|
4652
4250
|
def CMDdescription(parser, args):
|
4653
4251
|
"""Brings up the editor for the current CL's description."""
|
4654
4252
|
parser.add_option('-d', '--display', action='store_true',
|
@@ -4672,11 +4270,9 @@ def CMDdescription(parser, args):
|
|
4672
4270
|
if not target_issue_arg.valid:
|
4673
4271
|
parser.error('invalid codereview url or CL id')
|
4674
4272
|
|
4675
|
-
auth_config = auth.extract_auth_config_from_options(options)
|
4676
|
-
|
4677
4273
|
kwargs = {
|
4678
|
-
|
4679
|
-
|
4274
|
+
'auth_config': auth.extract_auth_config_from_options(options),
|
4275
|
+
'codereview': options.forced_codereview,
|
4680
4276
|
}
|
4681
4277
|
detected_codereview_from_url = False
|
4682
4278
|
if target_issue_arg:
|
@@ -4719,20 +4315,7 @@ def CMDdescription(parser, args):
|
|
4719
4315
|
return 0
|
4720
4316
|
|
4721
4317
|
|
4722
|
-
|
4723
|
-
"""Pulls out the commit log to use as a base for the CL description."""
|
4724
|
-
log_args = []
|
4725
|
-
if len(args) == 1 and not args[0].endswith('.'):
|
4726
|
-
log_args = [args[0] + '..']
|
4727
|
-
elif len(args) == 1 and args[0].endswith('...'):
|
4728
|
-
log_args = [args[0][:-1]]
|
4729
|
-
elif len(args) == 2:
|
4730
|
-
log_args = [args[0] + '..' + args[1]]
|
4731
|
-
else:
|
4732
|
-
log_args = args[:] # Hope for the best!
|
4733
|
-
return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args)
|
4734
|
-
|
4735
|
-
|
4318
|
+
@metrics.collector.collect_metrics('git cl lint')
|
4736
4319
|
def CMDlint(parser, args):
|
4737
4320
|
"""Runs cpplint on the current changelist."""
|
4738
4321
|
parser.add_option('--filter', action='append', metavar='-x,+y',
|
@@ -4788,6 +4371,7 @@ def CMDlint(parser, args):
|
|
4788
4371
|
return 0
|
4789
4372
|
|
4790
4373
|
|
4374
|
+
@metrics.collector.collect_metrics('git cl presubmit')
|
4791
4375
|
def CMDpresubmit(parser, args):
|
4792
4376
|
"""Runs presubmit tests on the current changelist."""
|
4793
4377
|
parser.add_option('-u', '--upload', action='store_true',
|
@@ -4864,7 +4448,7 @@ def GenerateGerritChangeId(message):
|
|
4864
4448
|
# entropy.
|
4865
4449
|
lines.append(message)
|
4866
4450
|
change_hash = RunCommand(['git', 'hash-object', '-t', 'commit', '--stdin'],
|
4867
|
-
stdin='\n'.join(lines))
|
4451
|
+
stdin=('\n'.join(lines)).encode())
|
4868
4452
|
return 'I%s' % change_hash.strip()
|
4869
4453
|
|
4870
4454
|
|
@@ -4932,6 +4516,7 @@ def cleanup_list(l):
|
|
4932
4516
|
|
4933
4517
|
|
4934
4518
|
@subcommand.usage('[flags]')
|
4519
|
+
@metrics.collector.collect_metrics('git cl upload')
|
4935
4520
|
def CMDupload(parser, args):
|
4936
4521
|
"""Uploads the current changelist to codereview.
|
4937
4522
|
|
@@ -4982,11 +4567,6 @@ def CMDupload(parser, args):
|
|
4982
4567
|
'can be applied multiple times'))
|
4983
4568
|
parser.add_option('-s', '--send-mail', action='store_true',
|
4984
4569
|
help='send email to reviewer(s) and cc(s) immediately')
|
4985
|
-
parser.add_option('--emulate_svn_auto_props',
|
4986
|
-
'--emulate-svn-auto-props',
|
4987
|
-
action="store_true",
|
4988
|
-
dest="emulate_svn_auto_props",
|
4989
|
-
help="Emulate Subversion's auto properties feature.")
|
4990
4570
|
parser.add_option('-c', '--use-commit-queue', action='store_true',
|
4991
4571
|
help='tell the commit queue to commit this patchset; '
|
4992
4572
|
'implies --send-mail')
|
@@ -5020,11 +4600,10 @@ def CMDupload(parser, args):
|
|
5020
4600
|
help='Run all tests specified by input_api.RunTests in all '
|
5021
4601
|
'PRESUBMIT files in parallel.')
|
5022
4602
|
|
5023
|
-
|
4603
|
+
parser.add_option('--no-autocc', action='store_true',
|
4604
|
+
help='Disables automatic addition of CC emails')
|
5024
4605
|
parser.add_option('--private', action='store_true',
|
5025
|
-
help='
|
5026
|
-
parser.add_option('--email', default=None,
|
5027
|
-
help='email address to use to connect to Rietveld')
|
4606
|
+
help='Set the review private. This implies --no-autocc.')
|
5028
4607
|
|
5029
4608
|
orig_args = args
|
5030
4609
|
auth.add_auth_options(parser)
|
@@ -5056,10 +4635,22 @@ def CMDupload(parser, args):
|
|
5056
4635
|
settings.GetIsGerrit()
|
5057
4636
|
|
5058
4637
|
cl = Changelist(auth_config=auth_config, codereview=options.forced_codereview)
|
4638
|
+
if not cl.IsGerrit():
|
4639
|
+
# Error out with instructions for repos not yet configured for Gerrit.
|
4640
|
+
print('=====================================')
|
4641
|
+
print('NOTICE: Rietveld is no longer supported. '
|
4642
|
+
'You can upload changes to Gerrit with')
|
4643
|
+
print(' git cl upload --gerrit')
|
4644
|
+
print('or set Gerrit to be your default code review tool with')
|
4645
|
+
print(' git config gerrit.host true')
|
4646
|
+
print('=====================================')
|
4647
|
+
return 1
|
4648
|
+
|
5059
4649
|
return cl.CMDUpload(options, args, orig_args)
|
5060
4650
|
|
5061
4651
|
|
5062
4652
|
@subcommand.usage('--description=<description file>')
|
4653
|
+
@metrics.collector.collect_metrics('git cl split')
|
5063
4654
|
def CMDsplit(parser, args):
|
5064
4655
|
"""Splits a branch into smaller branches and uploads CLs.
|
5065
4656
|
|
@@ -5077,6 +4668,18 @@ def CMDsplit(parser, args):
|
|
5077
4668
|
default=False,
|
5078
4669
|
help="List the files and reviewers for each CL that would "
|
5079
4670
|
"be created, but don't create branches or CLs.")
|
4671
|
+
parser.add_option("--cq-dry-run", action='store_true',
|
4672
|
+
help="If set, will do a cq dry run for each uploaded CL. "
|
4673
|
+
"Please be careful when doing this; more than ~10 CLs "
|
4674
|
+
"has the potential to overload our build "
|
4675
|
+
"infrastructure. Try to upload these not during high "
|
4676
|
+
"load times (usually 11-3 Mountain View time). Email "
|
4677
|
+
"infra-dev@chromium.org with any questions.")
|
4678
|
+
parser.add_option('-a', '--enable-auto-submit', action='store_true',
|
4679
|
+
default=True,
|
4680
|
+
help='Sends your change to the CQ after an approval. Only '
|
4681
|
+
'works on repos that have the Auto-Submit label '
|
4682
|
+
'enabled')
|
5080
4683
|
options, _ = parser.parse_args(args)
|
5081
4684
|
|
5082
4685
|
if not options.description_file:
|
@@ -5086,10 +4689,12 @@ def CMDsplit(parser, args):
|
|
5086
4689
|
return CMDupload(OptionParser(), args)
|
5087
4690
|
|
5088
4691
|
return split_cl.SplitCl(options.description_file, options.comment_file,
|
5089
|
-
Changelist, WrappedCMDupload, options.dry_run
|
4692
|
+
Changelist, WrappedCMDupload, options.dry_run,
|
4693
|
+
options.cq_dry_run, options.enable_auto_submit)
|
5090
4694
|
|
5091
4695
|
|
5092
4696
|
@subcommand.usage('DEPRECATED')
|
4697
|
+
@metrics.collector.collect_metrics('git cl commit')
|
5093
4698
|
def CMDdcommit(parser, args):
|
5094
4699
|
"""DEPRECATED: Used to commit the current changelist via git-svn."""
|
5095
4700
|
message = ('git-cl no longer supports committing to SVN repositories via '
|
@@ -5104,28 +4709,17 @@ CHERRY_PICK_BRANCH = 'git-cl-cherry-pick'
|
|
5104
4709
|
|
5105
4710
|
|
5106
4711
|
@subcommand.usage('[upstream branch to apply against]')
|
4712
|
+
@metrics.collector.collect_metrics('git cl land')
|
5107
4713
|
def CMDland(parser, args):
|
5108
4714
|
"""Commits the current changelist via git.
|
5109
4715
|
|
5110
4716
|
In case of Gerrit, uses Gerrit REST api to "submit" the issue, which pushes
|
5111
4717
|
upstream and closes the issue automatically and atomically.
|
5112
|
-
|
5113
|
-
Otherwise (in case of Rietveld):
|
5114
|
-
Squashes branch into a single commit.
|
5115
|
-
Updates commit message with metadata (e.g. pointer to review).
|
5116
|
-
Pushes the code upstream.
|
5117
|
-
Updates review and closes.
|
5118
4718
|
"""
|
5119
4719
|
parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks',
|
5120
4720
|
help='bypass upload presubmit hook')
|
5121
|
-
parser.add_option('-m', dest='message',
|
5122
|
-
help="override review description")
|
5123
4721
|
parser.add_option('-f', '--force', action='store_true', dest='force',
|
5124
4722
|
help="force yes to questions (don't prompt)")
|
5125
|
-
parser.add_option('-c', dest='contributor',
|
5126
|
-
help="external contributor for patch (appended to " +
|
5127
|
-
"description and used as author for git). Should be " +
|
5128
|
-
"formatted as 'First Last <email@example.com>'")
|
5129
4723
|
parser.add_option('--parallel', action='store_true',
|
5130
4724
|
help='Run all tests specified by input_api.RunTests in all '
|
5131
4725
|
'PRESUBMIT files in parallel.')
|
@@ -5138,19 +4732,6 @@ def CMDland(parser, args):
|
|
5138
4732
|
if not cl.IsGerrit():
|
5139
4733
|
parser.error('rietveld is not supported')
|
5140
4734
|
|
5141
|
-
if options.message:
|
5142
|
-
# This could be implemented, but it requires sending a new patch to
|
5143
|
-
# Gerrit, as Gerrit unlike Rietveld versions messages with patchsets.
|
5144
|
-
# Besides, Gerrit has the ability to change the commit message on submit
|
5145
|
-
# automatically, thus there is no need to support this option (so far?).
|
5146
|
-
parser.error('-m MESSAGE option is not supported for Gerrit.')
|
5147
|
-
if options.contributor:
|
5148
|
-
parser.error(
|
5149
|
-
'-c CONTRIBUTOR option is not supported for Gerrit.\n'
|
5150
|
-
'Before uploading a commit to Gerrit, ensure it\'s author field is '
|
5151
|
-
'the contributor\'s "name <email>". If you can\'t upload such a '
|
5152
|
-
'commit for review, contact your repository admin and request'
|
5153
|
-
'"Forge-Author" permission.')
|
5154
4735
|
if not cl.GetIssue():
|
5155
4736
|
DieWithError('You must upload the change first to Gerrit.\n'
|
5156
4737
|
' If you would rather have `git cl land` upload '
|
@@ -5159,80 +4740,8 @@ def CMDland(parser, args):
|
|
5159
4740
|
options.verbose, options.parallel)
|
5160
4741
|
|
5161
4742
|
|
5162
|
-
def PushToGitWithAutoRebase(remote, branch, original_description,
|
5163
|
-
git_numberer_enabled, max_attempts=3):
|
5164
|
-
"""Pushes current HEAD commit on top of remote's branch.
|
5165
|
-
|
5166
|
-
Attempts to fetch and autorebase on push failures.
|
5167
|
-
Adds git number footers on the fly.
|
5168
|
-
|
5169
|
-
Returns integer code from last command.
|
5170
|
-
"""
|
5171
|
-
cherry = RunGit(['rev-parse', 'HEAD']).strip()
|
5172
|
-
code = 0
|
5173
|
-
attempts_left = max_attempts
|
5174
|
-
while attempts_left:
|
5175
|
-
attempts_left -= 1
|
5176
|
-
print('Attempt %d of %d' % (max_attempts - attempts_left, max_attempts))
|
5177
|
-
|
5178
|
-
# Fetch remote/branch into local cherry_pick_branch, overriding the latter.
|
5179
|
-
# If fetch fails, retry.
|
5180
|
-
print('Fetching %s/%s...' % (remote, branch))
|
5181
|
-
code, out = RunGitWithCode(
|
5182
|
-
['retry', 'fetch', remote,
|
5183
|
-
'+%s:refs/heads/%s' % (branch, CHERRY_PICK_BRANCH)])
|
5184
|
-
if code:
|
5185
|
-
print('Fetch failed with exit code %d.' % code)
|
5186
|
-
print(out.strip())
|
5187
|
-
continue
|
5188
|
-
|
5189
|
-
print('Cherry-picking commit on top of latest %s' % branch)
|
5190
|
-
RunGitWithCode(['checkout', 'refs/heads/%s' % CHERRY_PICK_BRANCH],
|
5191
|
-
suppress_stderr=True)
|
5192
|
-
parent_hash = RunGit(['rev-parse', 'HEAD']).strip()
|
5193
|
-
code, out = RunGitWithCode(['cherry-pick', cherry])
|
5194
|
-
if code:
|
5195
|
-
print('Your patch doesn\'t apply cleanly to \'%s\' HEAD @ %s, '
|
5196
|
-
'the following files have merge conflicts:' %
|
5197
|
-
(branch, parent_hash))
|
5198
|
-
print(RunGit(['-c', 'core.quotePath=false', 'diff',
|
5199
|
-
'--name-status', '--diff-filter=U']).strip())
|
5200
|
-
print('Please rebase your patch and try again.')
|
5201
|
-
RunGitWithCode(['cherry-pick', '--abort'])
|
5202
|
-
break
|
5203
|
-
|
5204
|
-
commit_desc = ChangeDescription(original_description)
|
5205
|
-
if git_numberer_enabled:
|
5206
|
-
logging.debug('Adding git number footers')
|
5207
|
-
parent_msg = RunGit(['show', '-s', '--format=%B', parent_hash]).strip()
|
5208
|
-
commit_desc.update_with_git_number_footers(parent_hash, parent_msg,
|
5209
|
-
branch)
|
5210
|
-
# Ensure timestamps are monotonically increasing.
|
5211
|
-
timestamp = max(1 + _get_committer_timestamp(parent_hash),
|
5212
|
-
_get_committer_timestamp('HEAD'))
|
5213
|
-
_git_amend_head(commit_desc.description, timestamp)
|
5214
|
-
|
5215
|
-
code, out = RunGitWithCode(
|
5216
|
-
['push', '--porcelain', remote, 'HEAD:%s' % branch])
|
5217
|
-
print(out)
|
5218
|
-
if code == 0:
|
5219
|
-
break
|
5220
|
-
if IsFatalPushFailure(out):
|
5221
|
-
print('Fatal push error. Make sure your .netrc credentials and git '
|
5222
|
-
'user.email are correct and you have push access to the repo.\n'
|
5223
|
-
'Hint: run command below to diangose common Git/Gerrit credential '
|
5224
|
-
'problems:\n'
|
5225
|
-
' git cl creds-check\n')
|
5226
|
-
break
|
5227
|
-
return code
|
5228
|
-
|
5229
|
-
|
5230
|
-
def IsFatalPushFailure(push_stdout):
|
5231
|
-
"""True if retrying push won't help."""
|
5232
|
-
return '(prohibited by Gerrit)' in push_stdout
|
5233
|
-
|
5234
|
-
|
5235
4743
|
@subcommand.usage('<patch url or issue id or issue url>')
|
4744
|
+
@metrics.collector.collect_metrics('git cl patch')
|
5236
4745
|
def CMDpatch(parser, args):
|
5237
4746
|
"""Patches in a code review."""
|
5238
4747
|
parser.add_option('-b', dest='newbranch',
|
@@ -5363,6 +4872,7 @@ def GetTreeStatusReason():
|
|
5363
4872
|
return status['message']
|
5364
4873
|
|
5365
4874
|
|
4875
|
+
@metrics.collector.collect_metrics('git cl tree')
|
5366
4876
|
def CMDtree(parser, args):
|
5367
4877
|
"""Shows the status of the tree."""
|
5368
4878
|
_, args = parser.parse_args(args)
|
@@ -5379,6 +4889,7 @@ def CMDtree(parser, args):
|
|
5379
4889
|
return 0
|
5380
4890
|
|
5381
4891
|
|
4892
|
+
@metrics.collector.collect_metrics('git cl try')
|
5382
4893
|
def CMDtry(parser, args):
|
5383
4894
|
"""Triggers try jobs using either BuildBucket or CQ dry run."""
|
5384
4895
|
group = optparse.OptionGroup(parser, 'Try job options')
|
@@ -5474,16 +4985,6 @@ def CMDtry(parser, args):
|
|
5474
4985
|
return 1
|
5475
4986
|
|
5476
4987
|
patchset = cl.GetMostRecentPatchset()
|
5477
|
-
# TODO(tandrii): Checking local patchset against remote patchset is only
|
5478
|
-
# supported for Rietveld. Extend it to Gerrit or remove it completely.
|
5479
|
-
if not cl.IsGerrit() and patchset != cl.GetPatchset():
|
5480
|
-
print('Warning: Codereview server has newer patchsets (%s) than most '
|
5481
|
-
'recent upload from local checkout (%s). Did a previous upload '
|
5482
|
-
'fail?\n'
|
5483
|
-
'By default, git cl try uses the latest patchset from '
|
5484
|
-
'codereview, continuing to use patchset %s.\n' %
|
5485
|
-
(patchset, cl.GetPatchset(), patchset))
|
5486
|
-
|
5487
4988
|
try:
|
5488
4989
|
_trigger_try_jobs(auth_config, cl, buckets, options, patchset)
|
5489
4990
|
except BuildbucketResponseException as ex:
|
@@ -5492,6 +4993,7 @@ def CMDtry(parser, args):
|
|
5492
4993
|
return 0
|
5493
4994
|
|
5494
4995
|
|
4996
|
+
@metrics.collector.collect_metrics('git cl try-results')
|
5495
4997
|
def CMDtry_results(parser, args):
|
5496
4998
|
"""Prints info about try jobs associated with current CL."""
|
5497
4999
|
group = optparse.OptionGroup(parser, 'Try job results options')
|
@@ -5532,15 +5034,6 @@ def CMDtry_results(parser, args):
|
|
5532
5034
|
'Either upload first, or pass --patchset explicitly' %
|
5533
5035
|
cl.GetIssue())
|
5534
5036
|
|
5535
|
-
# TODO(tandrii): Checking local patchset against remote patchset is only
|
5536
|
-
# supported for Rietveld. Extend it to Gerrit or remove it completely.
|
5537
|
-
if not cl.IsGerrit() and patchset != cl.GetPatchset():
|
5538
|
-
print('Warning: Codereview server has newer patchsets (%s) than most '
|
5539
|
-
'recent upload from local checkout (%s). Did a previous upload '
|
5540
|
-
'fail?\n'
|
5541
|
-
'By default, git cl try-results uses the latest patchset from '
|
5542
|
-
'codereview, continuing to use patchset %s.\n' %
|
5543
|
-
(patchset, cl.GetPatchset(), patchset))
|
5544
5037
|
try:
|
5545
5038
|
jobs = fetch_try_jobs(auth_config, cl, options.buildbucket_host, patchset)
|
5546
5039
|
except BuildbucketResponseException as ex:
|
@@ -5554,6 +5047,7 @@ def CMDtry_results(parser, args):
|
|
5554
5047
|
|
5555
5048
|
|
5556
5049
|
@subcommand.usage('[new upstream branch]')
|
5050
|
+
@metrics.collector.collect_metrics('git cl upstream')
|
5557
5051
|
def CMDupstream(parser, args):
|
5558
5052
|
"""Prints or sets the name of the upstream branch, if any."""
|
5559
5053
|
_, args = parser.parse_args(args)
|
@@ -5575,6 +5069,7 @@ def CMDupstream(parser, args):
|
|
5575
5069
|
return 0
|
5576
5070
|
|
5577
5071
|
|
5072
|
+
@metrics.collector.collect_metrics('git cl web')
|
5578
5073
|
def CMDweb(parser, args):
|
5579
5074
|
"""Opens the current CL in the web browser."""
|
5580
5075
|
_, args = parser.parse_args(args)
|
@@ -5586,10 +5081,23 @@ def CMDweb(parser, args):
|
|
5586
5081
|
print('ERROR No issue to open', file=sys.stderr)
|
5587
5082
|
return 1
|
5588
5083
|
|
5589
|
-
|
5084
|
+
# Redirect I/O before invoking browser to hide its output. For example, this
|
5085
|
+
# allows to hide "Created new window in existing browser session." message
|
5086
|
+
# from Chrome. Based on https://stackoverflow.com/a/2323563.
|
5087
|
+
saved_stdout = os.dup(1)
|
5088
|
+
saved_stderr = os.dup(2)
|
5089
|
+
os.close(1)
|
5090
|
+
os.close(2)
|
5091
|
+
os.open(os.devnull, os.O_RDWR)
|
5092
|
+
try:
|
5093
|
+
webbrowser.open(issue_url)
|
5094
|
+
finally:
|
5095
|
+
os.dup2(saved_stdout, 1)
|
5096
|
+
os.dup2(saved_stderr, 2)
|
5590
5097
|
return 0
|
5591
5098
|
|
5592
5099
|
|
5100
|
+
@metrics.collector.collect_metrics('git cl set-commit')
|
5593
5101
|
def CMDset_commit(parser, args):
|
5594
5102
|
"""Sets the commit bit to trigger the Commit Queue."""
|
5595
5103
|
parser.add_option('-d', '--dry-run', action='store_true',
|
@@ -5620,6 +5128,7 @@ def CMDset_commit(parser, args):
|
|
5620
5128
|
return 0
|
5621
5129
|
|
5622
5130
|
|
5131
|
+
@metrics.collector.collect_metrics('git cl set-close')
|
5623
5132
|
def CMDset_close(parser, args):
|
5624
5133
|
"""Closes the issue."""
|
5625
5134
|
_add_codereview_issue_select_options(parser)
|
@@ -5638,6 +5147,7 @@ def CMDset_close(parser, args):
|
|
5638
5147
|
return 0
|
5639
5148
|
|
5640
5149
|
|
5150
|
+
@metrics.collector.collect_metrics('git cl diff')
|
5641
5151
|
def CMDdiff(parser, args):
|
5642
5152
|
"""Shows differences between local tree and last upload."""
|
5643
5153
|
parser.add_option(
|
@@ -5676,12 +5186,17 @@ def CMDdiff(parser, args):
|
|
5676
5186
|
return 0
|
5677
5187
|
|
5678
5188
|
|
5189
|
+
@metrics.collector.collect_metrics('git cl owners')
|
5679
5190
|
def CMDowners(parser, args):
|
5680
5191
|
"""Finds potential owners for reviewing."""
|
5681
5192
|
parser.add_option(
|
5682
5193
|
'--ignore-current',
|
5683
5194
|
action='store_true',
|
5684
5195
|
help='Ignore the CL\'s current reviewers and start from scratch.')
|
5196
|
+
parser.add_option(
|
5197
|
+
'--ignore-self',
|
5198
|
+
action='store_true',
|
5199
|
+
help='Do not consider CL\'s author as an owners.')
|
5685
5200
|
parser.add_option(
|
5686
5201
|
'--no-color',
|
5687
5202
|
action='store_true',
|
@@ -5721,15 +5236,23 @@ def CMDowners(parser, args):
|
|
5721
5236
|
[] if options.ignore_current else cl.GetReviewers(),
|
5722
5237
|
fopen=file, os_path=os.path,
|
5723
5238
|
disable_color=options.no_color,
|
5724
|
-
override_files=change.OriginalOwnersFiles()
|
5239
|
+
override_files=change.OriginalOwnersFiles(),
|
5240
|
+
ignore_author=options.ignore_self).run()
|
5725
5241
|
|
5726
5242
|
|
5727
|
-
def BuildGitDiffCmd(diff_type, upstream_commit, args):
|
5243
|
+
def BuildGitDiffCmd(diff_type, upstream_commit, args, allow_prefix=False):
|
5728
5244
|
"""Generates a diff command."""
|
5729
5245
|
# Generate diff for the current branch's changes.
|
5730
|
-
diff_cmd = ['-c', 'core.quotePath=false', 'diff',
|
5731
|
-
|
5732
|
-
|
5246
|
+
diff_cmd = ['-c', 'core.quotePath=false', 'diff', '--no-ext-diff']
|
5247
|
+
|
5248
|
+
if allow_prefix:
|
5249
|
+
# explicitly setting --src-prefix and --dst-prefix is necessary in the
|
5250
|
+
# case that diff.noprefix is set in the user's git config.
|
5251
|
+
diff_cmd += ['--src-prefix=a/', '--dst-prefix=b/']
|
5252
|
+
else:
|
5253
|
+
diff_cmd += ['--no-prefix']
|
5254
|
+
|
5255
|
+
diff_cmd += [diff_type, upstream_commit, '--']
|
5733
5256
|
|
5734
5257
|
if args:
|
5735
5258
|
for arg in args:
|
@@ -5747,6 +5270,7 @@ def MatchingFileType(file_name, extensions):
|
|
5747
5270
|
|
5748
5271
|
|
5749
5272
|
@subcommand.usage('[files or directories to diff]')
|
5273
|
+
@metrics.collector.collect_metrics('git cl format')
|
5750
5274
|
def CMDformat(parser, args):
|
5751
5275
|
"""Runs auto-formatting tools (clang-format etc.) on the diff."""
|
5752
5276
|
CLANG_EXTS = ['.cc', '.cpp', '.h', '.m', '.mm', '.proto', '.java']
|
@@ -5755,8 +5279,20 @@ def CMDformat(parser, args):
|
|
5755
5279
|
help='Reformat the full content of all touched files')
|
5756
5280
|
parser.add_option('--dry-run', action='store_true',
|
5757
5281
|
help='Don\'t modify any file on disk.')
|
5758
|
-
parser.add_option(
|
5759
|
-
|
5282
|
+
parser.add_option(
|
5283
|
+
'--python',
|
5284
|
+
action='store_true',
|
5285
|
+
default=None,
|
5286
|
+
help='Enables python formatting on all python files.')
|
5287
|
+
parser.add_option(
|
5288
|
+
'--no-python',
|
5289
|
+
action='store_true',
|
5290
|
+
dest='python',
|
5291
|
+
help='Disables python formatting on all python files. '
|
5292
|
+
'Takes precedence over --python. '
|
5293
|
+
'If neither --python or --no-python are set, python '
|
5294
|
+
'files that have a .style.yapf file in an ancestor '
|
5295
|
+
'directory will be formatted.')
|
5760
5296
|
parser.add_option('--js', action='store_true',
|
5761
5297
|
help='Format javascript code with clang-format.')
|
5762
5298
|
parser.add_option('--diff', action='store_true',
|
@@ -5797,7 +5333,7 @@ def CMDformat(parser, args):
|
|
5797
5333
|
diff_files = [x for x in diff_files if os.path.isfile(x)]
|
5798
5334
|
|
5799
5335
|
if opts.js:
|
5800
|
-
CLANG_EXTS.
|
5336
|
+
CLANG_EXTS.extend(['.js', '.ts'])
|
5801
5337
|
|
5802
5338
|
clang_diff_files = [x for x in diff_files if MatchingFileType(x, CLANG_EXTS)]
|
5803
5339
|
python_diff_files = [x for x in diff_files if MatchingFileType(x, ['.py'])]
|
@@ -5849,26 +5385,73 @@ def CMDformat(parser, args):
|
|
5849
5385
|
|
5850
5386
|
# Similar code to above, but using yapf on .py files rather than clang-format
|
5851
5387
|
# on C/C++ files
|
5852
|
-
|
5853
|
-
|
5854
|
-
|
5855
|
-
|
5856
|
-
|
5857
|
-
|
5858
|
-
|
5859
|
-
|
5860
|
-
|
5861
|
-
|
5862
|
-
|
5863
|
-
|
5864
|
-
|
5865
|
-
|
5866
|
-
|
5867
|
-
|
5388
|
+
py_explicitly_disabled = opts.python is not None and not opts.python
|
5389
|
+
if python_diff_files and not py_explicitly_disabled:
|
5390
|
+
depot_tools_path = os.path.dirname(os.path.abspath(__file__))
|
5391
|
+
yapf_tool = os.path.join(depot_tools_path, 'yapf')
|
5392
|
+
if sys.platform.startswith('win'):
|
5393
|
+
yapf_tool += '.bat'
|
5394
|
+
|
5395
|
+
# If we couldn't find a yapf file we'll default to the chromium style
|
5396
|
+
# specified in depot_tools.
|
5397
|
+
chromium_default_yapf_style = os.path.join(depot_tools_path,
|
5398
|
+
YAPF_CONFIG_FILENAME)
|
5399
|
+
# Used for caching.
|
5400
|
+
yapf_configs = {}
|
5401
|
+
for f in python_diff_files:
|
5402
|
+
# Find the yapf style config for the current file, defaults to depot
|
5403
|
+
# tools default.
|
5404
|
+
_FindYapfConfigFile(f, yapf_configs, top_dir)
|
5405
|
+
|
5406
|
+
# Turn on python formatting by default if a yapf config is specified.
|
5407
|
+
# This breaks in the case of this repo though since the specified
|
5408
|
+
# style file is also the global default.
|
5409
|
+
if opts.python is None:
|
5410
|
+
filtered_py_files = []
|
5411
|
+
for f in python_diff_files:
|
5412
|
+
if _FindYapfConfigFile(f, yapf_configs, top_dir) is not None:
|
5413
|
+
filtered_py_files.append(f)
|
5868
5414
|
else:
|
5869
|
-
|
5870
|
-
|
5871
|
-
|
5415
|
+
filtered_py_files = python_diff_files
|
5416
|
+
|
5417
|
+
# Note: yapf still seems to fix indentation of the entire file
|
5418
|
+
# even if line ranges are specified.
|
5419
|
+
# See https://github.com/google/yapf/issues/499
|
5420
|
+
if not opts.full and filtered_py_files:
|
5421
|
+
py_line_diffs = _ComputeDiffLineRanges(filtered_py_files, upstream_commit)
|
5422
|
+
|
5423
|
+
for f in filtered_py_files:
|
5424
|
+
yapf_config = _FindYapfConfigFile(f, yapf_configs, top_dir)
|
5425
|
+
if yapf_config is None:
|
5426
|
+
yapf_config = chromium_default_yapf_style
|
5427
|
+
|
5428
|
+
cmd = [yapf_tool, '--style', yapf_config, f]
|
5429
|
+
|
5430
|
+
has_formattable_lines = False
|
5431
|
+
if not opts.full:
|
5432
|
+
# Only run yapf over changed line ranges.
|
5433
|
+
for diff_start, diff_len in py_line_diffs[f]:
|
5434
|
+
diff_end = diff_start + diff_len - 1
|
5435
|
+
# Yapf errors out if diff_end < diff_start but this
|
5436
|
+
# is a valid line range diff for a removal.
|
5437
|
+
if diff_end >= diff_start:
|
5438
|
+
has_formattable_lines = True
|
5439
|
+
cmd += ['-l', '{}-{}'.format(diff_start, diff_end)]
|
5440
|
+
# If all line diffs were removals we have nothing to format.
|
5441
|
+
if not has_formattable_lines:
|
5442
|
+
continue
|
5443
|
+
|
5444
|
+
if opts.diff or opts.dry_run:
|
5445
|
+
cmd += ['--diff']
|
5446
|
+
# Will return non-zero exit code if non-empty diff.
|
5447
|
+
stdout = RunCommand(cmd, error_ok=True, cwd=top_dir)
|
5448
|
+
if opts.diff:
|
5449
|
+
sys.stdout.write(stdout)
|
5450
|
+
elif len(stdout) > 0:
|
5451
|
+
return_value = 2
|
5452
|
+
else:
|
5453
|
+
cmd += ['-i']
|
5454
|
+
RunCommand(cmd, cwd=top_dir)
|
5872
5455
|
|
5873
5456
|
# Dart's formatter does not have the nice property of only operating on
|
5874
5457
|
# modified chunks, so hard code full.
|
@@ -5939,6 +5522,7 @@ def GetDirtyMetricsDirs(diff_files):
|
|
5939
5522
|
|
5940
5523
|
|
5941
5524
|
@subcommand.usage('<codereview url or issue id>')
|
5525
|
+
@metrics.collector.collect_metrics('git cl checkout')
|
5942
5526
|
def CMDcheckout(parser, args):
|
5943
5527
|
"""Checks out a branch associated with a given Rietveld or Gerrit issue."""
|
5944
5528
|
_, args = parser.parse_args(args)
|
@@ -6002,13 +5586,42 @@ class OptionParser(optparse.OptionParser):
|
|
6002
5586
|
'-v', '--verbose', action='count', default=0,
|
6003
5587
|
help='Use 2 times for more debugging info')
|
6004
5588
|
|
6005
|
-
def parse_args(self, args=None,
|
6006
|
-
|
5589
|
+
def parse_args(self, args=None, _values=None):
|
5590
|
+
try:
|
5591
|
+
return self._parse_args(args)
|
5592
|
+
finally:
|
5593
|
+
# Regardless of success or failure of args parsing, we want to report
|
5594
|
+
# metrics, but only after logging has been initialized (if parsing
|
5595
|
+
# succeeded).
|
5596
|
+
global settings
|
5597
|
+
settings = Settings()
|
5598
|
+
|
5599
|
+
if not metrics.DISABLE_METRICS_COLLECTION:
|
5600
|
+
# GetViewVCUrl ultimately calls logging method.
|
5601
|
+
project_url = settings.GetViewVCUrl().strip('/+')
|
5602
|
+
if project_url in metrics_utils.KNOWN_PROJECT_URLS:
|
5603
|
+
metrics.collector.add('project_urls', [project_url])
|
5604
|
+
|
5605
|
+
def _parse_args(self, args=None):
|
5606
|
+
# Create an optparse.Values object that will store only the actual passed
|
5607
|
+
# options, without the defaults.
|
5608
|
+
actual_options = optparse.Values()
|
5609
|
+
_, args = optparse.OptionParser.parse_args(self, args, actual_options)
|
5610
|
+
# Create an optparse.Values object with the default options.
|
5611
|
+
options = optparse.Values(self.get_default_values().__dict__)
|
5612
|
+
# Update it with the options passed by the user.
|
5613
|
+
options._update_careful(actual_options.__dict__)
|
5614
|
+
# Store the options passed by the user in an _actual_options attribute.
|
5615
|
+
# We store only the keys, and not the values, since the values can contain
|
5616
|
+
# arbitrary information, which might be PII.
|
5617
|
+
metrics.collector.add('arguments', actual_options.__dict__.keys())
|
5618
|
+
|
6007
5619
|
levels = [logging.WARNING, logging.INFO, logging.DEBUG]
|
6008
5620
|
logging.basicConfig(
|
6009
5621
|
level=levels[min(options.verbose, len(levels) - 1)],
|
6010
5622
|
format='[%(levelname).1s%(asctime)s %(process)d %(thread)d '
|
6011
5623
|
'%(filename)s] %(message)s')
|
5624
|
+
|
6012
5625
|
return options, args
|
6013
5626
|
|
6014
5627
|
|
@@ -6018,10 +5631,6 @@ def main(argv):
|
|
6018
5631
|
(sys.version.split(' ', 1)[0],), file=sys.stderr)
|
6019
5632
|
return 2
|
6020
5633
|
|
6021
|
-
# Reload settings.
|
6022
|
-
global settings
|
6023
|
-
settings = Settings()
|
6024
|
-
|
6025
5634
|
colorize_CMDstatus_doc()
|
6026
5635
|
dispatcher = subcommand.CommandDispatcher(__name__)
|
6027
5636
|
try:
|
@@ -6042,8 +5651,5 @@ if __name__ == '__main__':
|
|
6042
5651
|
# unit testing.
|
6043
5652
|
fix_encoding.fix_encoding()
|
6044
5653
|
setup_color.init()
|
6045
|
-
|
5654
|
+
with metrics.collector.print_notice_and_exit():
|
6046
5655
|
sys.exit(main(sys.argv[1:]))
|
6047
|
-
except KeyboardInterrupt:
|
6048
|
-
sys.stderr.write('interrupted\n')
|
6049
|
-
sys.exit(1)
|