[
  {
    "path": ".editorconfig",
    "content": "# Defines the coding style for different editors and IDEs.\n# http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".git-hooks/pre_commit/master_hooks_match.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'fileutils'\n\nmodule Overcommit::Hook::PreCommit\n  # Ensures all master hooks have the same content.\n  #\n  # This is necessary because we can't use symlinks to link all the hooks in the\n  # template directory to the master `overcommit-hook` file, since symlinks are\n  # not supported on Windows.\n  class MasterHooksMatch < Base\n    def run\n      hooks_dir = File.join('template-dir', 'hooks')\n      master_hook = File.join(hooks_dir, 'overcommit-hook')\n      Dir.glob(File.join(hooks_dir, '*')).each do |hook_path|\n        unless FileUtils.compare_file(master_hook, hook_path)\n          return [\n            :fail,\n            \"Template directory hook '#{hook_path}' does not match '#{master_hook}'!\\n\" \\\n            \"Run `cp #{master_hook} #{hook_path}`\"\n          ]\n        end\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  overcommit:\n    timeout-minutes: 10\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: 3.3\n          bundler-cache: true\n\n      - name: Prepare environment\n        run: |\n          git config --global user.email \"gh-actions@example.com\"\n          git config --global user.name \"GitHub Actions\"\n          bundle exec overcommit --sign\n          bundle exec overcommit --sign pre-commit\n\n      - name: Run pre-commit checks\n        run: bundle exec overcommit --run\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  rspec:\n    timeout-minutes: 15\n    runs-on: ${{ matrix.os }}-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        ruby-version:\n          - \"2.6\"\n          - \"2.7\"\n          - \"3.0\"\n          - \"3.1\"\n          - \"3.2\"\n          - \"3.3\"\n        os:\n          - ubuntu\n          # At the moment of this commit various specs fail on Windows.\n          # Any contributor is welcome to fix them and enable the Windows build.\n          # Please see Issue #836 for more details.\n          # - windows\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Ruby ${{ matrix.ruby-version }}\n        uses: ruby/setup-ruby@v1\n        with:\n          ruby-version: ${{ matrix.ruby-version }}\n          bundler-cache: true\n\n      - name: Run tests\n        run: |\n          git config --global user.email \"gh-actions@example.com\"\n          git config --global user.name \"GitHub Actions\"\n          bundle exec rspec\n\n      - name: Code coverage reporting\n        uses: coverallsapp/github-action@v2\n        with:\n          github-token: ${{ secrets.github_token }}\n          flag-name: ruby${{ matrix.ruby-version }}-${{ matrix.os }}\n          parallel: true\n\n  finish:\n    needs: rspec\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Finalize code coverage report\n        uses: coverallsapp/github-action@v2\n        with:\n          github-token: ${{ secrets.github_token }}\n          parallel-finished: true\n"
  },
  {
    "path": ".gitignore",
    "content": "Gemfile.lock\ncoverage/\npkg/\n.bundle\n.idea\n.history/\n.vscode/\n"
  },
  {
    "path": ".overcommit.yml",
    "content": "gemfile: Gemfile\n\nPreCommit:\n  # Disabled since this causes spurious failures on AppVeyor builds\n  BrokenSymlinks:\n    enabled: false\n\n  BundleCheck:\n    enabled: true\n\n  ExecutePermissions:\n    enabled: true\n    exclude:\n      - 'bin/overcommit'\n      - 'libexec/**/*'\n      - 'template-dir/hooks/**/*'\n\n  HardTabs:\n    enabled: true\n\n  MasterHooksMatch:\n    enabled: true\n    quiet: true\n\n  RuboCop:\n    enabled: true\n    include:\n      - '**/*.gemspec'\n      - '**/*.rb'\n      - '**/Gemfile'\n      - template-dir/hooks/overcommit-hook\n\n  TrailingWhitespace:\n    enabled: true\n\n  YamlSyntax:\n    enabled: true\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "inherit_from: .rubocop_todo.yml\n\nAllCops:\n  TargetRubyVersion: 2.6\n  NewCops: disable\n  SuggestExtensions: false\n\nLayout/ClosingParenthesisIndentation:\n  Enabled: false\n\nLayout/DotPosition:\n  EnforcedStyle: trailing\n\n# Fails on AppVeyor builds\nLayout/EndOfLine:\n  Enabled: false\n\nLayout/FirstParameterIndentation:\n  Enabled: false\n\nLayout/FirstArrayElementIndentation:\n  Enabled: false\n\nLayout/HeredocIndentation:\n  Enabled: false\n\nLayout/LineLength:\n  Max: 100\n\nLayout/MultilineMethodCallIndentation:\n  Enabled: false\n\nLayout/MultilineOperationIndentation:\n  Enabled: false\n\nLayout/SpaceBeforeFirstArg:\n  Exclude:\n    - '*.gemspec'\n\nLint/AmbiguousBlockAssociation:\n  Enabled: false\n\nLint/AmbiguousRegexpLiteral:\n  Enabled: false\n\nLint/AssignmentInCondition:\n  Enabled: false\n\nLint/Void:\n  Enabled: false\n\nMetrics/AbcSize:\n  Enabled: false\n\nMetrics/BlockLength:\n  Enabled: false\n\nMetrics/MethodLength:\n  Max: 20\n\nMetrics/ModuleLength:\n  Enabled: false\n\nNaming/FileName:\n  Exclude:\n    - 'template-dir/hooks/*'\n    - 'Gemfile'\n    - 'Rakefile'\n    - '*.gemspec'\n\n# Renaming `has_something?` to `something?` obfuscates whether it is a \"is-a\" or\n# a \"has-a\" relationship.\nNaming/PredicatePrefix:\n  Enabled: false\n\n# commit_sha1 is indeed how we want to write such a variable, so ignore this cop\nNaming/VariableNumber:\n  Enabled: false\n\n# Enforcing this results in a lot of unnecessary indentation.\nStyle/ClassAndModuleChildren:\n  Enabled: false\n\nStyle/Documentation:\n  Exclude:\n    - 'spec/overcommit/**/*'\n\nStyle/FormatString:\n  Enabled: false\n\n# There are a number of situations where this makes code less readable or\n# disrupts the visual flow of code.\nStyle/GuardClause:\n  Enabled: false\n\nStyle/IfUnlessModifier:\n  Enabled: false\n\n# We want to allow multi-line lambdas using the `->` syntax which Rubocop\n# doesn't allow. We're also not too worried about people using `lambda` for\n# single-line lambdas either.\nStyle/Lambda:\n  Enabled: false\n\nStyle/Next:\n  Enabled: false\n\n# Calling .zero? instead of comparing `== 0` seems unnecessarily verbose\nStyle/NumericPredicate:\n  Enabled: false\n\nStyle/ParallelAssignment:\n  Enabled: false\n\n# Prefer curly braces except for %i/%w/%W, since those return arrays.\nStyle/PercentLiteralDelimiters:\n  PreferredDelimiters:\n    '%': '{}'\n    '%i': '[]'\n    '%q': '{}'\n    '%Q': '{}'\n    '%r': '{}'\n    '%s': '()'\n    '%w': '[]'\n    '%W': '[]'\n    '%x': '{}'\n\nStyle/RescueModifier:\n  Exclude:\n    - 'bin/overcommit'\n\nStyle/SignalException:\n  Enabled: false\n\n# Forcing a particular name (e.g. |a, e|) for inject methods prevents you from\n# choosing intention-revealing names.\nStyle/SingleLineBlockParams:\n  Enabled: false\n\nStyle/SpecialGlobalVars:\n  Enabled: false\n\nStyle/SymbolArray:\n  Enabled: false\n\nStyle/TrailingCommaInArguments:\n  Enabled: false\n\nStyle/TrailingCommaInArrayLiteral:\n  Enabled: false\n\nStyle/TrailingCommaInHashLiteral:\n  Enabled: false\n"
  },
  {
    "path": ".rubocop_todo.yml",
    "content": "# This configuration was generated by\n# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit`\n# on 2024-01-10 14:09:00 UTC using RuboCop version 1.59.0.\n# The point is for the user to remove these configuration records\n# one by one as the offenses are removed from the code base.\n# Note that changes in the inspected code, or installation of new\n# versions of RuboCop, may require this file to be generated again.\n\n# Offense count: 1\n# This cop supports safe autocorrection (--autocorrect).\n# Configuration parameters: AllowAliasSyntax, AllowedMethods.\n# AllowedMethods: alias_method, public, protected, private\nLayout/EmptyLinesAroundAttributeAccessor:\n  Exclude:\n    - 'lib/overcommit/hook_context/post_merge.rb'\n\n# Offense count: 6\n# Configuration parameters: AllowedMethods.\n# AllowedMethods: enums\nLint/ConstantDefinitionInBlock:\n  Exclude:\n    - 'spec/overcommit/message_processor_spec.rb'\n\n# Offense count: 4\nLint/MixedRegexpCaptureTypes:\n  Exclude:\n    - 'lib/overcommit/hook/pre_commit/dart_analyzer.rb'\n    - 'lib/overcommit/hook/pre_commit/java_checkstyle.rb'\n    - 'lib/overcommit/hook/pre_commit/kt_lint.rb'\n    - 'lib/overcommit/hook/pre_commit/scalastyle.rb'\n\n# Offense count: 2\n# This cop supports safe autocorrection (--autocorrect).\nLint/RedundantCopDisableDirective:\n  Exclude:\n    - 'lib/overcommit/hook_runner.rb'\n    - 'lib/overcommit/printer.rb'\n\n# Offense count: 1\n# Configuration parameters: CountComments, Max, CountAsOne.\nMetrics/ClassLength:\n  Exclude:\n    - 'lib/overcommit/utils.rb'\n\n# Offense count: 2\n# Configuration parameters: AllowedMethods, AllowedPatterns, Max.\nMetrics/CyclomaticComplexity:\n  Exclude:\n    - 'lib/overcommit/configuration.rb'\n    - 'lib/overcommit/hook_runner.rb'\n\n# Offense count: 3\n# Configuration parameters: AllowedMethods, AllowedPatterns, Max.\nMetrics/PerceivedComplexity:\n  Exclude:\n    - 'lib/overcommit/configuration.rb'\n    - 'lib/overcommit/configuration_validator.rb'\n    - 'lib/overcommit/hook_runner.rb'\n\n# Offense count: 23\n# This cop supports unsafe autocorrection (--autocorrect-all).\nStyle/GlobalStdStream:\n  Exclude:\n    - 'bin/overcommit'\n    - 'lib/overcommit/hook/post_commit/git_guilt.rb'\n    - 'template-dir/hooks/commit-msg'\n    - 'template-dir/hooks/overcommit-hook'\n    - 'template-dir/hooks/post-checkout'\n    - 'template-dir/hooks/post-commit'\n    - 'template-dir/hooks/post-merge'\n    - 'template-dir/hooks/post-rewrite'\n    - 'template-dir/hooks/pre-commit'\n    - 'template-dir/hooks/pre-push'\n    - 'template-dir/hooks/pre-rebase'\n    - 'template-dir/hooks/prepare-commit-msg'\n\n# Offense count: 2\n# This cop supports unsafe autocorrection (--autocorrect-all).\nStyle/HashTransformValues:\n  Exclude:\n    - 'lib/overcommit/configuration.rb'\n    - 'lib/overcommit/configuration_validator.rb'\n\n# Offense count: 1\n# Configuration parameters: AllowedMethods.\n# AllowedMethods: respond_to_missing?\nStyle/OptionalBooleanParameter:\n  Exclude:\n    - 'lib/overcommit/logger.rb'\n\n# Offense count: 2\n# This cop supports safe autocorrection (--autocorrect).\nStyle/RedundantBegin:\n  Exclude:\n    - 'lib/overcommit/hook/prepare_commit_msg/replace_branch.rb'\n    - 'lib/overcommit/utils.rb'\n\n# Offense count: 10\n# This cop supports unsafe autocorrection (--autocorrect-all).\n# Configuration parameters: SafeForConstants.\nStyle/RedundantFetchBlock:\n  Exclude:\n    - 'lib/overcommit/configuration.rb'\n    - 'lib/overcommit/configuration_validator.rb'\n    - 'lib/overcommit/hook/base.rb'\n    - 'lib/overcommit/hook/pre_commit/chamber_verification.rb'\n    - 'lib/overcommit/logger.rb'\n    - 'spec/support/shell_helpers.rb'\n\n# Offense count: 8\n# This cop supports safe autocorrection (--autocorrect).\nStyle/RedundantRegexpEscape:\n  Exclude:\n    - 'lib/overcommit/configuration.rb'\n    - 'lib/overcommit/hook/pre_commit/php_cs.rb'\n    - 'lib/overcommit/hook/pre_commit/php_lint.rb'\n    - 'lib/overcommit/hook/pre_commit/php_stan.rb'\n\n# Offense count: 15\n# This cop supports unsafe autocorrection (--autocorrect-all).\n# Configuration parameters: Mode.\nStyle/StringConcatenation:\n  Exclude:\n    - 'lib/overcommit/hook/pre_commit/bundle_check.rb'\n    - 'lib/overcommit/hook_runner.rb'\n    - 'lib/overcommit/message_processor.rb'\n    - 'spec/integration/gemfile_option_spec.rb'\n    - 'spec/overcommit/hook/commit_msg/text_width_spec.rb'\n    - 'spec/overcommit/hook/prepare_commit_msg/base_spec.rb'\n    - 'spec/overcommit/message_processor_spec.rb'\n    - 'spec/spec_helper.rb'\n\n# Offense count: 1\n# This cop supports unsafe autocorrection (--autocorrect-all).\nStyle/ZeroLengthPredicate:\n  Exclude:\n    - 'lib/overcommit/hook/pre_commit/ruby_syntax.rb'\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Overcommit Changelog\n\n## 0.68.0\n\n* Add `Solargraph` pre-commit hook\n\n## 0.67.1\n\n* Fix `set` gem dependency error when running with `--diff` flag\n\n## 0.67.0\n\n* Fix bug introduced in 0.65.0 that prevented `gemfile: false` from working correctly\n\n## 0.66.0\n\n* Add `--diff` CLI option for running pre-commit hooks against only changed files\n\n## 0.65.0\n\n* Load bundled gems on expected version\n\n## 0.64.1\n\n* Update minimum version of rexml to address [CVE-2024-49761](https://www.ruby-lang.org/en/news/2024/10/28/redos-rexml-cve-2024-49761/)\n\n## 0.64.0\n\n* Add support for `stylelint` 16+\n* Add `changelog_uri` to gemspec\n\n## 0.63.0\n\n* Add `Sorbet` pre-commit hook\n* Add `RSpec` pre-commit hook\n\n## 0.62.0\n\n* Allow version 5 of `childprocess` gem dependency\n\n## 0.61.0\n\n* Allow `ReplaceBranch` to use `skip_if`\n* Fix local Overcommit file merges with existing `.overcommit.yml`\n\n## 0.60.0\n\n* Allow overriding `Gemfile.lock` location for `BundleCheck` pre-commit hook\n* Fix `ReplaceBranch` prepare-commit-msg hook to allow trailing spaces\n* Add `MixFormat` pre-commit hook\n* Add `MixTest` pre-push hook\n* Allow loading custom local configuration from `.local-overcommit.yml`\n* Handle `Psych::DisallowedClass` when running `YamlSyntax` pre-commit hook\n* Add support for specifying custom `encoding` in `RailsSchemaUpToDate` pre-commit hook\n\n## 0.59.1\n\n* Remove `--disable-pending-cops` as default flag to `RuboCop` pre-commit hook.\n* Remove special handling of process output on Windows since it broke on Linux.\n\n## 0.59.0\n\n* Add `--disable-pending-cops` as default flag to `RuboCop` pre-commit hook to ignore non-existent cops. Requires RuboCop `0.82.0` or newer.\n* Fix deprecation warning for `Bundler.with_clean_env`.\n* Fix handling of some kinds of pronto errors in the `Pronto` hook.\n* Fix encoding of process output on Windows.\n* Add support for specifying hook type to `--run` flag.\n* Fix message regex parser for Stylelint.\n* Fix configuration loading on Ruby 3.1.\n* Fix `YamlSyntax` to support aliases when parsing.\n* Fix run output to explicitly flush partial logs.\n\n## 0.58.0\n\n* Add `rexml` dependency explicitly to support Ruby 3.0.\n* Add `DartAnalyzer` pre-commit hook to analyze Dart files.\n* Add `PubTest` and `FlutterTest` pre-push hooks to run `pub test` and `flutter test` for Dart projects, respectively.\n* Update `index-tags` script to support scanning only files tracked by Git.\n* Fix `EsLint` pre-commit hook to not report certain false positives.\n* Update `YamlLint` to `fail` the run instead of `warn` when errors are detected.\n* Update `YamlLint` parse the line number of output so it is line aware.\n* Gracefully handle breaking behavior in upstream Psych gem to support YAML aliases.\n* Fix case where `git` would delete all tracked files when popping stash.\n\n## 0.57.0\n\n* Fix `CommitMsg` hooks to be able to call `modified_lines_in_file`.\n* Add `ErbLint` pre-commit hook to lint ERB files.\n\n## 0.56.0\n\n* Update `ReplaceBranch` prepare-commit-msg hook to avoid running on `--amend` by default.\n* Add support for `modified_files` and `modified_lines_in_file` in `CommitMsg` hooks.\n\n## 0.55.0\n\n* Fix `GoFmt` to not be enabled by default. This was enabled by mistake when introduced in Overcommit `0.52.0`.\n\n## 0.54.1\n\n* Fix `Overcommit::GitRepo.list_files` helper to work with arbitrarily large lists of files.\n* Fix `AuthorName` to allow mononyms to be more inclusive of names.\n\n## 0.54.0\n\n* Fix `YamlLint` pre-commit hook\n* Relax `childprocess` gem version constraint to allow version 4.x\n\n## 0.53.0\n\n* Improve performance in `PhpCs` pre-commit hook\n* Add `Pronto` pre-push hook\n* Remove erroneous extra newline in replacement string for `ReplaceBranch` prepare-commit-msg hook\n* Add note about potentially checking your stash when hook is interrupted\n* Add support for skipping hooks based on command result using the `skip_if` option\n\n## 0.52.1\n\n* Fix case where no standard input is provided to `pre-push` hooks\n\n## 0.52.0\n\n* Fix `Mdl` to properly parse JSON output from `mdl`\n* Add `GolangciLint` pre-commit and pre-push hooks\n* Add `GoTest` pre-push hook\n* Add `GoFmt` pre-commit hook\n* Add `exclude_branches` hook option to disable hooks running on specific branches\n* Add `exclude_remotes` pre-push hook option to disable pre-push hooks running against specific remotes\n* Change default behavior of pre-push hooks to **not** run against deleted remote refs\n* Add `include_remote_ref_deletions` pre-push hook option to allow running for a remote branch deletion\n* Rename `remote_branch_deletion?` pre-push hook helper to `remote_ref_deletion?`\n* Add per-branch `destructive_only` setting to `ProtectedBranches` pre-push hook\n\n## 0.51.0\n\n* Stop stashing in pre-commit hooks when all changes are already staged,\n  avoiding unnecessary file modification\n* Improve instructions for recovering commit message when a `commit-msg` hook\n  fails\n\n## 0.50.0\n\n* Fix Overcommit to display helpful error message when a hook does not inherit\n  from the base class\n* Relax `childprocess` gem constraint to allow up to version 3.x\n* Display a helpful message if hooks do not inherit from the correct base class\n* Fix Overcommit to work with emacs/magit by [disabling literal pathspecs](https://magit.vc/manual/magit/My-Git-hooks-work-on-the-command_002dline-but-not-inside-Magit.html)\n\n## 0.49.1\n\n* Fix Overcommit to run when executed with no parent process\n* Fix `Stylelint` pre-commit hook `required_executable`\n\n## 0.49.0\n\n### New Features\n\n* Add `skipped_commit_types` option to `ReplaceBranch` prepare-commit-msg hook\n* Add `RubySyntax` pre-commit hook\n* Add `CodeSpellCheck` pre-commit hook\n\n### Changes\n\n* Relax `childprocess` dependency to allow version 1.x\n\n### Bug Fixes\n* Fix deadlock which was more likely to occur when setting `parallelize` on a hook to `false`\n* Fix `Mdl` hook to use JSON output and not fail on unexpected output\n\n## 0.48.1\n\n* Fix `Stylelint` hook regex to extract line numbers with more than one digit\n* Fix `CaseConflicts` hook to work with file paths containing double quotes\n\n## 0.48.0\n\n* Drop support for Ruby 2.3 or older\n* Support multi-line matches in `MessageFormat` `commit-msg` hook\n* Add `FileSize` pre-commit hook\n\n## 0.47.0\n\n### New Features\n\n* Add support for `prepare-commit-message` hooks\n* Add [`SwiftLint`](https://github.com/realm/SwiftLint) pre-commit hook\n* Add [`KtLint`](https://github.com/shyiko/ktlint) pre-commit hook\n* Add `TerraformFormat` pre-commit hook\n* Add [`CookStyle`](https://docs.chef.io/cookstyle.html) pre-commit hook\n\n### Changes\n\n* Update `validator_uri` for `W3cHtml` pre-commit hook\n* Update `TsLint` pre-commit hook to support new output format\n* Update `BundleCheck` error message with additional instructions\n\n### Bug Fixes\n\n* Add `--force-exclusion` flag to `Reek` pre-commit hook configuration to\n  ensure excluded files are excluded\n\n## 0.46.0\n\n* Fix `Credo` pre-commit hook to lint applicable files only rather than\n  all files\n* Add `PhpCsFixer` pre-commit hook\n* Add `YardCoverage` pre-commit hook\n* Add `Flay` pre-commit hook\n* Add `Stylelint` pre-commit hook\n* Fix `TsLint` default flags to work with `tslint` 5.11+\n\n## 0.45.0\n\n### New Features\n\n* Add `CargoTest` pre-push hook for running `cargo test`\n* Add `min_subject_width` option to `TextWidth` `commit-msg` hook\n\n### Changes\n\n* Drop support for Ruby versions 2.1 and older\n\n### Bug Fixes\n\n* Fix detection of `.git` directory location on Git versions before 2.5\n\n## 0.44.0\n\n### New Features\n\n* Add support for [worktrees](https://git-scm.com/docs/git-worktree)\n\n### Bug Fixes\n\n* Fix installer to not attempt to remove old hooks directory if non-empty\n* Fix erroneous `fatal` error message from a pre-commit hook run when adding\n  the first submodule to a repo\n\n## 0.43.0\n\n### Changes\n\n* Add [`GitLfs`](https://git-lfs.github.com/) `post-checkout`, `post-commit`\n  and `post-merge` hooks\n* Display commit message when `commit-msg` hooks fail\n* Drop support for JRuby\n* Enhance `pre-push` hooks to expose `modified_lines_in_file`, similar to\n  `pre-commit` hooks\n* Add `YarnCheck` pre-commit hook which checks if `yarn.lock` matches `package.json`\n* Add [`PhpUnit`](https://phpunit.de/) `pre-push` hook\n\n## 0.42.0\n\n### New Features\n\n* Add `YarnInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add [`metadata-json-lint`](https://voxpupuli.org/blog/2014/11/06/linting-metadata-json/) pre-commit hook\n* Add [`RstLint`](https://github.com/twolfson/restructuredtext-lint) pre-commit\n  hook\n* Add `YarnInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add additional file patterns for `ChamberSecurity` pre-commit hook\n* Add `ChamberCompare` and `ChamberVerification` pre-commit hooks\n* Add `ComposerInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add ability to `pre-push` hooks to inspect modified files for pushed refs\n* Add [`PhpStan`](https://github.com/phpstan/phpstan) pre-commit hook\n\n### Changes\n\n* Run `GoLint` pre-commit hook against each file individually\n* Improve performance of `BundleAudit` checking of `Gemfile.lock` file\n* Allow ad hoc hooks to run executables not tracked by Git\n* Drop support for Ruby 2.0\n\n### Bug Fixes\n\n* Fix `LineEndings` pre-commit hook handling of file paths with spaces\n* Fix `Mdl` pre-commit hook message parsing regex\n* Fix `RailsBestPractices` hook to only run against changed files\n* Fix Overcommit installation in submodules\n* Don't print backtrace of signature change for `overcommit --run`\n\n## 0.41.0\n\n* Add [`PhpCs`](http://pear.php.net/package/PHP_CodeSniffer) pre-commit hook\n* Add [`PhpLint`](http://php.net/manual/en/features.commandline.options.php)\n  pre-commit hook\n* Allow toggling colorize output via `OVERCOMMIT_COLOR` environment variable\n\n## 0.40.0\n\n* Add [`Pronto`](https://github.com/mmozuras/pronto) pre-commit hook\n* Add [`hadolint`](https://github.com/lukasmartinelli/hadolint) pre-commit hook\n* Add [`license_finder`](https://github.com/pivotal/LicenseFinder) pre-commit hook\n* Use the `core.hooksPath` Git configuration option when installing hooks\n* Gracefully handle binary files in `LineEndings` pre-commit hook\n* Relax `childprocess` dependency to allow 0.x\n* Gracefully handle gem loading errors when invoking Overcommit in a repo where\n  the `gemfile` specified by the local `.overcommit.yml` references a gem\n  version incompatible with the already-loaded Overcommit\n* Ignore `Makefile` and `*.go` files in `HardTabs` pre-commit hook by default\n\n## 0.39.1\n\n### Bug Fixes\n\n* Update `childprocess` to 0.6.3\n\n## 0.39.0\n\n### New Features\n\n* Add [`GitLfs`](https://git-lfs.github.com/) pre-push hook\n\n### Changes\n\n* Update `childprocess` dependency to 0.6.x series\n* Auto-sign configuration file when installing hooks for the first time\n\n### Bug Fixes\n\n* Fix `forwarding to private method` warning on Ruby 2.4.x\n* Fix potential hang when a hook's `parallelize` option was set to `false`\n* Fix `empty strings as pathspecs` warning introduced in Git 2.11\n\n## 0.38.0\n\n### New Features\n\n* Add `Pytest` pre-push hook\n* Add `RakeTarget` pre-commit and pre-push hook\n* Moved `CommitPlease` from `CommitMsg` to `PostCommit` hook\n* Add `skip_file_checkout` hook setting for `PostCheckout` hooks\n\n### Bug Fixes\n\n* Fix `install_command` for scss_lint gem\n\n## 0.37.0\n\n### New Features\n\n* Add `FixMe` pre-commit hook, to ensure that no \"token\" words slips through.\n  These strings are things you should fix now, not later\n* Add [`YAMLLint`](https://github.com/adrienverge/yamllint) pre-commit hook\n* Add `LicenseHeader` pre-commit enforcement to ensure open source projects\n  contain proper license comments\n* Add [`Foodcritic`](http://www.foodcritic.io/) pre-commit hook\n* Add `LineEndings` pre-commit hook that allows you to enforcing UNIX- or\n  Windows-style line endings\n\n### Bug Fixes\n\n* Fix `CapitalizedSubject` to not fail when commit message starts with one or\n  more empty lines\n\n## 0.36.0\n\n* Add [`Fasterer`](https://github.com/DamirSvrtan/fasterer) pre-commit hook\n* Add [`Brakeman`](http://brakemanscanner.org/) pre-push hook\n* Add [`TSLint`](http://palantir.github.io/tslint/) pre-commit hook\n* Validate that hook `env` environment configurations have valid names/values\n* Fix a false negative reported by RailsSchemaUpToDate for newly-created Rails\n  projects that don't yet have any migrations\n\n## 0.35.0\n\n* Drop support for Ruby 1.9.3\n* Fix `JavaCheckstyle` pre-commit hook to properly categorize `INFO` and\n  `WARN` messages\n* Add `TestUnit` pre-push hook to run tests with `Test::Unit`\n* Add `BundleAudit` pre-commit hook to scan gems for vulnerabilities with\n  [`bundle-audit`](https://github.com/rubysec/bundler-audit)\n* Copy hook files instead of symlinking\n* Add `Credo` pre-commit hook to check Elixir files\n* Remove `Brakeman` pre-commit hook as it could erroneously report clean\n  runs depending on which files were committed to your repository. You\n  should run this tool in a separate job/task in your CI runs as it doesn't\n  make for a good pre-commit hook.\n* Add `Commitplease` pre-commit hook which checks commit messages with\n  [`commitplease`](https://www.npmjs.com/package/commitplease)\n\n## 0.34.2\n\n* Add `--no-color` flag to all `git diff`/`git show` calls to override local\n  configuration\n* Ignore `commit.gpgsign` configuration option when creating stash commits\n  in pre-commit hooks\n\n## 0.34.1\n\n* Switch template directory hooks from symlinks to regular files so gem can\n  be installed on Windows\n\n## 0.34.0\n\n* Fix `Scalastyle` pre-commit hook to capture messages with no line number\n* Fix `CoffeeLint` pre-commit hook detection of modified lines\n* Fix `Jscs` pre-commit hook to work with `jscs` 3.0.0+\n* Fix `CapitalizedSubject` pre-commit hook to ignore commit message subjects\n  starting with `fixup!` or `squash!` special prefixes\n* Add `BundleOutdated` pre-commit hook to report gems in the `Gemfile.lock`\n  that have newer versions available\n* Add `destructive_only` option to `ProtectedBranches` pre-push hook\n* Include `.ru` files in `RuboCop` pre-commit hook\n* Fix `TextWidth` to ignore special `fixup!`/`squash!` prefixes in commit\n  message subjects when determining width of line\n\n## 0.33.0\n\n### New Features\n\n* Add global `quiet` option which silences all hook output except in the case\n  of warning or error\n\n### Changes\n\n* Official support for Rubinius has been dropped. It will probably still work\n  for most use cases, but parallelized hook runs may be problematic. If someone\n  from the community is willing to step up to support it, we'll gladly add it\n  back\n* Change `overcommit` CLI to automatically run within a Bundler context if the\n  `gemfile` option is specified. This mainly saves you from needing\n  `bundle exec` when running `overcommit --run`\n\n### Bug Fixes\n\n* Fix `AuthorName`/`AuthorEmail` pre-commit hooks to respect\n  `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL` environment variables, respectively\n* Fix `JavaCheckstyle` pre-commit hook to ignore `[ERROR]` prefix when parsing\n  output messages\n\n## 0.32.0\n\n### New Features\n\n* Hooks are now run in parallel by default\n* Add `concurrency` global option allowing you to specify the number of threads\n  to use when running hooks concurrently\n* Add `parallelize` hook option which specifies whether or not this hook should\n  be run in parallel (default is `true`)\n* Add `processors` hook option allowing you to specify how many processing\n  units a hook should require\n* Add `ForbiddenBranches` pre-commit hook which prevents creating a commit\n  on any blacklisted branch by name/pattern\n* Add `MessageFormat` commit-msg hook to validate commit messages against\n  a regex pattern\n\n### Changes\n\n* Improve error message output when there is a problem processing messages\n  via `extract_messages` pre-commit hook helper\n* Switch `ScssLint` pre-commit hook to use the JSON output formatter instead\n  of the default formatter\n* Change tense of hook descriptions from progressive indicative form (\"Running\")\n  to indicative present form (\"Run\") so output reads better in parallel hook\n  runs\n\n### Bug Fixes\n\n* Fix bug where amending a commit with command line arguments containing\n  Unicode characters could cause a crash due to invalid byte sequences\n* Fix `Minitest` pre-push hook to include all test files\n\n## 0.32.0.rc1\n\n* Add `concurrency` global option allowing you to specify the number of threads\n  to use when running hooks concurrently\n* Add `parallelize` hook option which specifies whether or not this hook should\n  be run in parallel (default is `true`)\n* Add `processors` hook option allowing you to specify how many processing\n  units a hook should require\n\n## 0.31.0\n\n* Add support for glob patterns to `ProtectedBranches` pre-push hook\n* Add `Mdl` pre-commit hook to run\n  [`mdl`](https://github.com/mivok/markdownlint) on Markdown files\n* Add `--without-color` flag to `RailsBestPractices` pre-commit hook\n  to fix parsing issues due to color escape sequences\n* Improve error message when `gemfile` has not had a dependency installed\n* Fix `RuboCop` pre-commit hook to not swallow cop messages when `parser` gem\n  warnings are output to STDERR\n\n## 0.30.0\n\n### New Features\n\n* Add `Dogma` pre-commit hook to lint Elixir files with\n  [dogma](http://elixir-lang.org/) files\n* Add `Minitest` pre-push hook to run Minitest tests\n* Add `RailsBestPractices` pre-commit hook which lints code with\n  [`rails_best_practices`](https://github.com/railsbp/rails_best_practices)\n\n### Bug Fixes\n\n* Fix `--run` flag to not block reading STDIN when using existing hook scripts\n* Fix `RuboCop` pre-commit hook to fail when RuboCop version specified by\n  Bundler context is not available\n* Fix `TextWidth` commit-msg hook to not include newline characters in\n  calculated width\n\n## 0.29.1\n\n* Raise error when hooks are defined with invalid names (i.e. non-alphanumeric\n  characters)\n* Fix hook signing when specifying hook name\n* Fix `BundleCheck` pre-commit hook to not report false negatives when running\n  via `overcommit --run` with local changes\n\n## 0.29.0\n\n### Important Security Fix\n\n* Fix vulnerability where disabling signature verification would not be caught\n  by signature verification, allowing an attacker to bypass the check. If you\n  disable signature verification in your configuration, you must rename the\n  option to `verify_signatures` and should audit your hooks.\n\n### New Features\n\n* Allow nested arrays in `include` and `exclude` options so lists of file\n  glob patterns can be shared across hook configurations via YAML references\n* Add `NginxTest` pre-commit hook that checks nginx configuration files with\n  [`nginx -t`](https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/)\n* Respect `core.commentchar` configuration when reading commit messages\n\n### Changes\n\n* Rename `verify_plugin_signatures` to `verify_signatures`\n\n### Bug Fixes\n\n* Fix `Jscs` pre-commit hook to handle the new `jscs`\n  [exit codes](https://github.com/jscs-dev/node-jscs/wiki/Exit-codes) introduced\n  as of 2.2.0\n* Fix `Scalastyle` pre-commit hook to fail with non-zero exit statuses\n\n## 0.28.0\n\n* Ensure `applicable_files` hook helper returns files in lexicographic order\n* Add `NpmInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add `PuppetLint` pre-commit hook that checks Puppet code with\n  [puppet-lint](http://puppet-lint.com/)\n* Add `BowerInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add `BundleInstall` post-checkout, post-commit, post-merge, and post-rewrite hooks\n* Add `Sqlint` pre-commit hook that checks SQL code with\n  [sqlint](https://github.com/purcell/sqlint)\n* Add Windows support\n* Add `Hlint` pre-commit hook that checks Haskell files with\n  [hlint](https://github.com/ndmitchell/hlint)\n* Add `ExecutePermissions` pre-commit hook that checks file mode for\n  unnecessary execute permissions\n\n## 0.27.0\n\n### New Features\n\n* Add `HtmlHint` pre-commit hook that checks HTML files with\n  [HTMLHint](http://htmlhint.com/)\n* Add support to the hook `execute` helper for accepting an optional list of\n  splittable command arguments for transparently dealing with really long file\n  lists and the operating system command length limit\n* Add `modified_files` helper to `PostCheckout` and `PostRewrite` hooks\n* Add `rewritten_commits` helper to `PostRewrite` hooks\n* Add `gemfile` option to configuration file which allows a `Gemfile` to be\n  loaded by Bundler to enforce particular gem versions during hook runs\n* Add support for `OVERCOMMIT_DEBUG` environment variable which toggles the\n  display of additional verbose output from executed commands\n* Add support for defining\n  [hooks based on your existing git hooks](README.md#adding-existing-git-hooks)\n  within your `.overcommit.yml` (no Ruby code required)\n* Add support for filtering all hooks except a small list via the `ONLY`\n  environment variable (similar to `SKIP` except a whitelist instead of\n  blacklist)\n\n### Changes\n\n* Don't display \"No applicable _hook-type_ hooks to run\" message unless debug\n  mode is enabled\n\n### Bug Fixes\n\n* Fix pre-commit hook bug where amending a commit which breaks a symlink would\n  result in that symlink not being included in the list of modified files\n* Fix `CaseConflicts` pre-commit hook handling of large sets of files\n* Fix `SemiStandard`/`Standard` hooks to read from `STDOUT` instead of `STDERR`\n  and handle new output format\n* Fix `commit-msg` hooks to handle large commit messages auto-generated by the\n  `--verbose` flag for `git commit`\n\n## 0.26.0\n\n### New Features\n\n* Add `EmptyMessage` commit-msg hook that reports commits messages that are\n  empty or contain only whitespace\n* Add `env` hook configuration option that allows you to set values for\n  environment variables during the course of a particular hook's run\n\n### Bug Fixes\n\n* Fix handling of paths with spaces in the name\n* Fix `CaseConflicts` pre-commit hook to not fail on initial commit\n* Fix handling of files removed or renamed in a commit amendment\n\n## 0.25.0\n\n### New Features\n\n* Add `Vint` pre-commit hook that checks Vim script with\n  [Vint](https://github.com/Kuniwak/vint)\n* Add `Scalariform` pre-commit hook that checks formatting of Scala code with\n  [Scalariform](https://mdr.github.io/scalariform/)\n* Add `SlimLint` pre-commit hook that analyzes Slim templates with\n  [Slim-Lint](https://github.com/sds/slim-lint)\n\n### Changes\n\n* Include SVG files in `ImageOptim`, `XmlLint`, and `XmlSyntax` pre-commit\n  hooks by default\n* Make `IndexTags` hooks quiet by default\n* Rename `Rubocop` pre-commit hook to `RuboCop` to match the project's proper\n  name\n\n### Bug Fixes\n\n* Fix `HardTabs` and `TrailingWhitespace` pre-commit hooks to include\n  line information in errors, making it work as expected when\n  `problem_on_unmodified_line` is set to something other than `report`\n* Fix handling of changing a symlink to a directory on commit amendment so it\n  is not included in the list of modified files for pre-commit hooks\n* Handle empty commit messages in `CapitalizedSubject`, `SingleLineSubject`,\n  `HardTabs`, `TextWidth`, and `TrailingPeriod` commit-msg hooks\n\n## 0.24.0\n\n### New Features\n\n* Add `required_library`/`required_libraries` hook option which specifies\n  a list of paths a hook should load with `Kernel.require` before running\n* Add `JsLint` pre-commit hook that checks the style of JavaScript files with\n  [JSLint](http://www.jslint.com/)\n* Add `RubyLint` pre-commit hook that statically analyzes Ruby files with\n  [ruby-lint](https://github.com/YorickPeterse/ruby-lint)\n* Add `Jsl` pre-commit hook that checks the style of JavaScript files with\n  [JavaScript Lint](http://www.javascriptlint.com/)\n* Add `CapitalizedSubject` commit message hook\n* Add `GoVet` pre-commit hook that examines Go source files with\n  [vet](https://godoc.org/golang.org/x/tools/cmd/vet)\n* Add `XmlSyntax` pre-commit hook to check that XML files are valid\n* Add `CaseConflicts` pre-commit hook which checks for file names in the same\n  directory which differ by letter casing\n* Preserve existing git hooks in a repository when installing Overcommit hooks,\n  and restore them on uninstall\n* Add `RSpec` pre-push hook that runs [RSpec](http://rspec.info/) tests before\n  pushing to remote\n* Add `ProtectedBranches` pre-push hook that prevents destructive pushes\n  (deletions or force pushes) to specified branches\n* Add `SpellCheck` commit-msg hook to check commit messages for misspelled words\n* Add support for `pre-rebase` hooks\n* Add `SubmoduleStatus` `post-checkout`, `post-commit`, `post-merge`, and\n  `post-rewrite` hooks that warn when submodules are uninitialized, out of date\n  with the current index, or contain merge conflicts\n\n### Changes\n\n* Disable `ShellCheck` pre-commit hook by default\n* Switch `ImageOptim` hook to use executable instead of Ruby API\n* Improve `CoffeeLint` pre-commit hook to differentiate between errors and\n  warnings\n* Improve `GoLint` pre-commit hook to extract file and line information\n* Change configuration loading behavior to prefer user-defined `ALL` hook\n  configuration over default `ALL` configuration, and user-defined hook\n  configuration over default `ALL` configuration\n* Change hook summary message to mention warnings if there were any\n* Disable almost all hooks by default. You will now need to explicitly enable\n  almost all hooks yourself in your `.overcommit.yml`. If you are migrating from\n  `overcommit` 0.23.0 and want to use the default configuration that shipped\n  with that version, copy the [default configuration from 0.23.0](https://github.com/sds/overcommit/blob/9f03e9c82b385d375a836ca7146b117dbde5c822/config/default.yml)\n* Update `ScssLint` pre-commit hook to properly handle special exit code that\n  signals all files were filtered by exclusions (new as of `scss-lint` 0.36.0)\n* Update `childprocess` dependency to minimum 0.5.6\n* Change default value for `problem_on_unmodified_line` from `warn` to `report`\n* Update `Rubocop` pre-commit hook to pass `--display-cop-names` flag so\n  cop names appear in output\n* Drop support for returning `:good`/`:bad` results from hooks (was deprecated in\n  0.15.0)\n* Remove `PryBinding` pre-commit hook since its functionality is provided by the\n  `Rubocop` pre-commit hook\n\n### Bug Fixes\n\n* Fix `LocalPathsInGemfile` to not report lints for commented paths\n* Fix `CssLint` pre-commit hook to ignore blank lines in `csslint` output\n* Fix error instructions typo in `BundleCheck` pre-commit hook\n* Fix bug where stashed changes were not restored when plugin signature\n  validation failed\n* Don't clear working tree after pre-commit hook when only submodule changes\n  are present\n* Restore file modification times of unstaged files in addition to staged files\n  in pre-commit hook runs\n\n## 0.23.0\n\n### New Features\n\n* Add pre-commit [ESLint](http://eslint.org/) hook\n* Add pre-commit hooks for [standard](https://github.com/feross/standard) and\n  [semistandard](https://github.com/Flet/semistandard) JavaScript linters\n* Add support for `post-commit`, `post-merge`, and `post-rewrite` hooks\n* Add `GitGuilt` `post-commit` hook to display changes in blame ownership for\n  modified files\n* Add `execute_in_background` helper to provide a standardized way to start\n  long-running processes without blocking the hook run\n* Add `IndexTags` hook for `post-commit`, `post-merge`, and `post-rewrite`\n  hook types so tags index can always be kept up to date via `ctags`\n* Add `W3cCss` and `W3cHtml` pre-commit hooks which integrate with the\n  `w3c_validator` gem\n* Add `Scalastyle` pre-commit hook that runs\n  [scalastyle](http://www.scalastyle.org/) against Scala code\n* Add `XmlLint` pre-commit hook to check XML files with\n  [xmllint](http://xmlsoft.org/xmllint.html)\n* Add `JavaCheckstyle` pre-commit hook to check style of Java files with\n  [checkstyle](http://checkstyle.sourceforge.net/)\n* Add `Pep8` pre-commit hook to check Python files with\n  [pep8](https://pypi.python.org/pypi/pep8)\n* Add `Pyflakes` pre-commit hook to check Python files with\n  [pyflakes](https://pypi.python.org/pypi/pyflakes)\n* Add `Pep257` pre-commit hook to check Python files with\n  [pep257](https://pypi.python.org/pypi/pep257)\n* Add `HtmlTidy` pre-commit hook to check HTML files with\n  [tidy](http://www.html-tidy.org/)\n* Add `Pylint` pre-commit hook to check Python files with\n  [pylint](http://www.pylint.org/)\n\n### Changes\n\n* Parse JSHint errors more precisely\n* Remove `JsxHint` and `Jsxcs` pre-commit hooks in favor of using the\n  `required_executable` option on the JsHint and Jscs pre-commit hooks\n* Change behavior of configuration options containing array values to always\n  replace the old value instead of appending to it\n* Change `ImageOptim` hook to fail instead of warn if the `image_optim` gem\n  cannot be found\n* Remove `ctags_arguments` option from `IndexTags` hooks\n* Improve `PythonFlake8` pre-commit hook to differentiate between errors\n  and warnings\n* Improve `CssLint` pre-commit hook to differentiate between errors and\n  warnings\n\n### Bug Fixes\n\n* Fix `--run` flag to consider all lines in all files as modified rather than none\n* Fix `--run` flag to exclude submodule directories from the list of modified files\n* Fix handling of files with spaces in their name when calculating modified\n  lines in a file\n\n## 0.22.0\n\n* Disable `Reek` pre-commit hook by default\n* Allow `required_executable` to include paths that are in the repo root\n* Add `command` hook option allowing the actual command that is executed\n  to be configured (useful to invoke command via `bundle exec` or similar)\n* Add `flags` hook option allowing the flags passed on the command line\n  to be configured\n\n## 0.21.0\n\n* Change `HardTabs`, `MergeConflicts`, and `PryBinding` pre-commit hooks to\n  be `quiet` by default\n* Switch `TravisLint` pre-commit hook from deprecated `travis-lint` gem to\n  `travis` gem\n* Add .projections.json configuration file\n* Add pre-commit static analysis and linting for sh/bash scripts with\n  [ShellCheck](http://www.shellcheck.net/)\n* Use `--verbose` flag when running JSCS to include name of offending rule\n\n## 0.20.0\n\n* Add `--run` flag which runs all configured pre-commit hooks against the\n  entire repository\n* Fix installer to work with Overcommit hooks created via `GIT_TEMPLATE_DIR`\n* Fix hook runner to not display skip message unless hook would have actually\n  run\n* Change `ImageOptim` hook to use `skip_missing_workers` option and update\n  dependency to 0.18.0\n* Remove interactive prompt support from overcommit hooks\n* Change hook signing from interactive flow to be done via\n  `overcommit --sign <hook-type>` command\n\n## 0.19.0\n* Add `--no-pngout` flag for `image_optim` command on `:fail` message\n* Fix `Brakeman` pre-commit hook when multiple files have been staged\n* Reset modification times more frequently when cleaning up the environment\n  after running pre-commit hooks. This should help overcommit work with file\n  watchers a little more nicely.\n* Add pre-commit JavaScript style checking with\n  [JSXCS](https://github.com/orktes/node-jsxcs)\n* Add pre-commit Ruby code smell checking with\n  [Reek](https://github.com/troessner/reek)\n* Gracefully handle `.git` files that point to an external git directory\n\n## 0.18.0\n\n* Update minimum version of `image_optim` gem to 0.15.0 (breaking change in\n  name of exception classes)\n* Add `--list-hooks` flag which displays all hooks for a repository and\n  whether they are enabled/disabled\n* Add `required_executable` and `install_command` options that allow a hook\n  to define an executable that must be in the `PATH` in order for it to work,\n  and a command the user can use to install the executable if it doesn't exist\n* All built-in hooks will now fail if the required executable is not present\n* Fix bug where pre-commit hook would crash if user attempted to commit a\n  broken symlink\n* Add `BrokenSymlinks` pre-commit hook which checks for broken symlinks\n* Fix Chamber integration\n* Fix 'include' path for ChamberSecurity\n* Fix bug where commit message from cherry-picked commit would be lost if\n  there were conflicts\n\n## 0.17.0\n\n* Change commit hook header text to bold instead of bold white so that it\n  displays on terminals with a white background\n* Add support for `OVERCOMMIT_DISABLE` environment variable, which when set\n  prevents Overcommit hooks from running\n* Fix bug that prevented RailsSchemaUpToDate from working in directories that\n  contained decimals\n* Warn when trying to pipe commands using the `execute` helper, as this is not\n  supported\n* Include both standard out/error streams in exception messages in pre-commit\n  hook context\n\n## 0.16.0\n\n* Fix edge case where hitting Ctrl-C twice rapidly could result in work\n  tree being lost\n* Fix edge case where hitting Ctrl-C after all pre-commit hooks had run\n  but before the cleanup had finished would result in a lost working\n  tree\n* Handle edge case where if a file was created in the working directory by a\n  separate process in between the working tree being reset and the stash being\n  applied, the hook runner would silently fail\n* Prevent stack traces from appearing during early interrupt before Overcommit\n  has loaded its code\n* Remove `BundleCheck` post-checkout hook as it was a bit overzealous\n\n## 0.15.0\n\n* Fix bug where incorrect \"hook run interrupted\" message displayed when\n  hook run failed\n* Gracefully handle `git stash` failures in pre-commit hook runs\n* Fix `overcommit-hook` auto-updating not passing original arguments to\n  updated hook\n* Display message when `overcommit-hook` file is automatically updated\n* Deprecate `:bad` status in favor of `:fail`\n* Deprecate `:good` status in favor of `:pass`\n* Allow hook statuses to be transformed via `on_fail` and `on_warn`\n  configuration options\n* Add `config` attribute as the preferred method to access hook\n  configurations in hook implementations\n* Generate starter configuration on install with instructions on how to\n  configure overcommit if an `.overcommit.yml` file does not yet exist\n* Include name of hook in output (to make it easier to find out which name\n  to use when skipping)\n\n## 0.14.1\n\n* Fix hook skipping regression\n\n## 0.14.0\n\n* Ignore `db/structure.sql` in `TrailingWhitespace` pre-commit hook\n* Drop stashed changes after restoring them (now that #55 is fixed)\n* Change `JSCS` pre-commit hook to check status code instead of using\n  regex to determine type of error\n* Fix performance regression where running Overcommit in a repository\n  with a lot of files would be very slow\n* Wildcards in include/exclude globs now match files beginning with `.`\n* Drop support for Ruby 1.8.7\n\n## 0.13.0\n\n* Prevent `JsonSyntax` pre-commit hook from failing if `json_class` key\n  is present in JSON\n* Prevent `HardTabs` pre-commit hook from warning on tabs in Makefiles\n* Fix bug where `overcommit` hooks would fail for initial commit to repo\n* Add support for gracefully exiting from Ctrl-C interruptions\n* Add `.gitmodules` to the list of ignored files in `HardTabs` pre-commit hook\n\n## 0.12.0\n\n* Skip `HardTabs` pre-commit hook for Golang source files by default\n* Disable `IndexTags` post-checkout hook by default\n* Add `GoLint` pre-commit hook which runs `golint` on Golang source files\n\n## 0.11.1\n\n* Fix bug where `CHERRY_PICK_HEAD` would be lost when a pre-commit hook failed\n  after attempting to cherry pick a commit with a conflict\n* Drop support for Ruby 1.9.2\n\n## 0.11.0\n\n* Allow custom arguments to be passed to `ctags` via `IndexTags` post-checkout\n  hook\n\n## 0.10.0\n\n* Change format of `include`/`exclude` file globs to match that of standard\n  shell globbing (e.g. `**` matches zero or more directories rather than 1 or\n  more)\n* Don't drop stashed changes after restoring them\n* Fix bug where `MERGE_HEAD` would be lost when attempting to commit a\n  resolution to a merge conflict\n\n## 0.9.0\n\n* Include `--force-exclusion` flag in Rubocop hook so files excluded via\n  `.rubocop.yml` are actually excluded\n* Add pre-commit `JsxHint` hook which uses the\n  [JSXHint](https://github.com/STRML/JSXHint) project\n* Add pre-commit `BerksfileCheck` hook which warns you when your\n  `Berksfile.lock` is out of sync with your `Berksfile`\n* Fix `BundleCheck` to use `git ls-files` instead of `git check-ignore`,\n  as the latter is only available as of git 1.8\n* Fix bug where skipping a hook via the `SKIP` environment variable would\n  incorrectly warn about the hook's configuration having changed\n* Add `MergeConflicts` pre-commit hook which checks for unresolved merge\n  conflicts in files\n* Add `RailsSchemaUpToDate` pre-commit hook which checks for\n  `schema.rb`/`structure.sql` that aren't up-to-date with the latest migration\n* Add `PryBinding` pre-commit hook which checks for `binding.pry` calls that\n  have been left behind in code\n* Add `LocalPathsInGemfile` pre-commit hook which checks for gem dependencies\n  pointing to local paths in a `Gemfile`\n* Add `JsonSyntax` pre-commit hook which checks the syntax of all `.json` files\n* Add `Brakeman` pre-commit hook which runs security checks against code\n  (disabled by default as it is slow)\n* Add `ChamberSecurity` pre-commit hook which ensures that `chamber secure` has\n  been run before committing your changes (see the\n  [Chamber](https://github.com/thekompanee/chamber) gem for more information)\n\n## 0.8.0\n\n* Add pre-commit `TravisLint` hook which uses the\n  [travis-lint](https://github.com/travis-ci/travis-lint) gem\n* Display actual warning message when dependencies aren't satisfied in\n  post-checkout `BundleCheck` hook\n* Add support for hook plugin signature verification so that you don't\n  automatically execute repo-specific hooks that changed since you last\n  ran them. See [Security](README.md#security) for more information\n* Automatically update `overcommit-hook` master hook and any other symlinks\n  before hook run. Run `overcommit --install` if you're upgrading to save\n  you from having to run `overcommit --install` in the future\n\n## 0.7.0\n\n* Change `command` hook helper signature to accept an array of arguments\n  instead of a shell string\n* Rename `command` hook helper to `execute`\n* Add support for JRuby 1.7.9 in Ruby 1.9 mode\n* Display more helpful error message when installing Overcommit into a repo\n  that already has non-Overcommit hooks\n* Add `--force` flag allowing Overcommit to be installed in repositories that\n  already contain non-Overcommit hooks (overwriting them in the process)\n\n## 0.6.3\n\n* `TextWidth` pre-commit hook now supports customized maximum subject line\n  and commit message body widths\n* Fix bug where committing a change with only file deletions would result\n  in those changes not being committed\n* Warn instead of failing when gem dependencies are out of date in\n  `BundleCheck` post-checkout hook\n\n## 0.6.2\n\n* Fix bug where hook run would crash if hook was unsuccessful but returned no\n  output\n\n## 0.6.1\n\n* Fix bug where a plugin would fail to load if it had a custom configuration\n  defined\n\n## 0.6.0\n\n* Breaking changes: plugin framework has been overhauled.\n  You must now subclass `Overcommit::Hook::<Type>` and implement\n  the method `run` instead of `run_check`. Also, the old hook runner\n  no longer works, so you'll need to remove the hooks installed in\n  `.git/hooks` and install new ones with `overcommit --install`\n* Configuration for repository can be specified via `.overcommit.yml` file\n* Can now skip hooks using just `SKIP` instead of `SKIP_CHECKS` environment\n  variable\n* Add `--template-dir` flag which provides a convenient way to auto-install\n  overcommit via Git template directories\n* Converted all script-based hook scripts to Ruby-based ones\n* `AuthorEmail` check can be customized so emails match a regex\n* `Whitespace` check was split into `HardTabs` and `TrailingWhitespace`\n* Add pre-commit JavaScript style checking with\n  [JSCS](https://github.com/mdevils/node-jscs)\n* Add `BundleCheck` pre-commit hook which checks if `Gemfile.lock` matches\n  `Gemfile`\n\n## 0.5.0\n\n* Use per-file `.scss-lint.yml` configuration for staged files\n\n## 0.4.1\n\n* Remove `RestrictedPaths` pre-commit check\n\n## 0.4.0\n\n* Added pre-commit check that optimizes images with `image_optim`\n* Don't include submodules in the list of modified files\n\n## 0.3.2\n\n* Fix bug where `.rubocop.yml` would not be found in present working directory\n\n## 0.3.1\n\n* Use per-file `.rubocop.yml` configuration for staged files\n\n## 0.3.0\n\n* Added Gemfile.lock/bundler checking\n* Added `--no-ext-diff` option to git diff\n* Exposed StagedFile#original_path\n\n## 0.2.6\n\n* Added check for linting HAML files with\n  [haml-lint](https://github.com/sds/haml-lint)\n\n## 0.2.5\n\n* Don't use `--silent` flag with `rubocop` for Ruby style check\n  (allows upgrade to Rubocop 0.12.0)\n\n## 0.2.4\n\n* Teach scss-lint check to downgrade lints on untouched lines as warnings\n\n## 0.2.3\n\n* Fix \"Too many open files\" error for very large commits\n* Make `StagedFile` tempfile creation lazy - should speed up some checks\n* Address rare cross-platform compatibility issue by replacing a `which`\n  call with a pure Ruby equivalent\n* Fix CoffeeScript linter path processing\n\n## 0.2.2\n\n* Allow specifying multiple file types for checks and syntax check rake files\n* Fix bug where checks which returned lists of lines would output incorrectly\n* Indent check output lines to nest under check name for better readability\n\n## 0.2.1\n\n* Fix bug where checks that didn't return strings for output would error\n\n## 0.2.0\n\n* Teach `StagedFile`s how to calculate which lines were actually added/modified\n* Checks no longer need to filter temporary staged file paths themselves\n* Condense Ruby style check output\n* Teach Ruby style check to downgrade style lints on untouched lines as warnings\n\n## 0.1.11\n\n* Added Ruby code style linting via RuboCop\n\n## 0.1.10\n\n* Fixed bug where `output` was expected to be a string but was an array in\n  js_syntax\n\n## 0.1.9\n\n* Fixed bug where `staged` helper in `HookSpecificCheck` wasn't returning\n  `StagedFile`s\n\n## 0.1.8\n\n* Resurrect StagedFile for reading index contents rather than disk contents\n\n## 0.1.7\n\n* Sort plugins alphabetically\n* Omit periods from warning messages for consistency\n* Enforce single-line commit message subjects\n* Only colorize output when logging to a TTY\n* Add check to detect hard tabs in commit messages\n* Fix crashing --list-templates flag\n\n## 0.1.6\n\n* Strip out blank release note in addition to warning the committer\n* Add Python linting via [flake8](http://flake8.readthedocs.org/en/latest/)\n* Add CoffeeScript linting via [coffeelint](http://www.coffeelint.org/)\n\n## 0.1.5\n\n* Improve spec coverage\n* Use installed `jshint` if available instead of Rhino\n* Update readme with dependencies, uninstall instructions\n\n## 0.1.4\n\n* Fix SKIP_CHECKS for namespaced hooks\n* Make hooks work when repo-specific configuration file is missing\n* Improve error handling when loading custom hooks\n\n## 0.1.3\n\n* Add un-skippable checks (not skipped via SKIP_CHECKS)\n* Improve spec coverage\n\n## 0.1.2\n\n* Add uninstall (-u) option\n\n## 0.1.1\n\n* Make installer more robust\n* Improve readme documentation\n* Add template listing (-l) to CLI\n* Add rspec and super-basic spec coverage\n* Improve command-line messaging\n\n## 0.1.0\n\n* First public release\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Overcommit\n\n## Bug Reports\n\n* Ensure that your issue [has not already been reported][1]. It may already be\n  fixed!\n* Include the steps you carried out to produce the problem.\n* Include the behavior you observed along with the behavior you expected, and\n  why you expected it.\n* Try setting the `OVERCOMMIT_DEBUG` environment variable to enable the display\n  of additional verbose output from executed commands.\n* Include the stack trace and any debugging output reported by Overcommit.\n\n## Feature Requests\n\nWe welcome feedback with or without pull requests. If you have an idea for how\nto improve the tool, great! All we ask is that you take the time to write a\nclear and concise explanation of what need you are trying to solve. If you have\nthoughts on _how_ it can be solved, include those too!\n\nThe best way to see a feature added, however, is to submit a pull request.\n\n## Pull Requests\n\n* Before creating your pull request, it's usually worth asking if the code\n  you're planning on writing will actually be considered for merging. You can\n  do this by [opening an issue][1] and asking. It may also help give the\n  maintainers context for when the time comes to review your code.\n\n* Ensure your [commit messages are well-written][2]. This can double as your\n  pull request message, so it pays to take the time to write a clear message.\n\n* Add tests for your feature. You should be able to look at other tests for\n  examples, especially if you're contributing a pre-commit hook.\n\n  Speaking of tests, we use `rspec`, which can be run like so:\n\n  ```bash\n  bundle exec rspec\n  ```\n\n* Submit your pull request!\n\nAll pull requests will be tested against [Travis CI][3], where the following\ncommands are run against multiple versions of Ruby:\n\n```bash\nbundle exec rspec\nbundle exec overcommit --run\n```\n\nEnsuring your changes pass for the above commands before submitting your pull\nrequest will save you time having to fix those changes. Better yet, if you\n[install Overcommit](README.md#installation) hooks into your forked repo, a lot\nof these checks will be done automatically for you!\n\n### Naming Hooks\n\nHooks should be named in camel case format (e.g. `RuboCop`) with acronyms only\ncapitalizing the first letter in the series (e.g. SCSS Lint becomes `ScssLint`).\n\nIf a tool has a specific capitalization that is odd, follow that capitalization.\nFor example, `Scalastyle` is written with a lowercase \"s\" rather than\ncamel-cased as `ScalaStyle`, so the `Scalastyle` hook follows that convention.\nExceptions to this rule are tools that begin with a lowercase\nletter&mdash;these should be capitalized.\n\nLastly, unless a tool has a particularly unique or descriptive name, include\nan additional prefix to help categorize it (e.g. `Java` in `JavaCheckstyle`),\nso it is easier for others to find hooks in the [README](README.md).\n\nThe reasoning for this perhaps odd naming scheme is to strike a balance between\nconsistency, familiarity for those who already know the tool, and Overcommit's\nability to deduce the name of a hook from its filename and vice versa.\n\n[1]: https://github.com/sds/overcommit/issues\n[2]: https://medium.com/brigade-engineering/the-secrets-to-great-commit-messages-106fc0a92a25\n[3]: https://travis-ci.org/\n\n## Code of conduct\n\nThis project adheres to the [Open Code of Conduct][code-of-conduct]. By\nparticipating, you are expected to honor this code.\n\n[code-of-conduct]: https://github.com/civiccc/code-of-conduct\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource 'https://rubygems.org'\n\ngemspec\n\n# Development dependencies are listed below\n\ngem 'rspec', '~> 3.0'\n\ngem 'simplecov', '~> 0.21.0'\ngem 'simplecov-lcov', '~> 0.8.0'\n\n# Pin RuboCop for CI builds\nif RUBY_VERSION < '2.7.0'\n  gem 'rubocop', '1.50.0'\nelse\n  gem 'rubocop', '1.77.0'\nend\n\ngem 'ffi' if Gem.win_platform?\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2013-2019 Shane da Silva, Aiden Scandella\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Gem Version](https://badge.fury.io/rb/overcommit.svg)](https://badge.fury.io/rb/overcommit)\n[![Build Status](https://github.com/sds/overcommit/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/sds/overcommit/actions/workflows/tests.yml/badge.svg?branch=main)\n[![Coverage Status](https://coveralls.io/repos/github/sds/overcommit/badge.svg?branch=main)](https://coveralls.io/github/sds/overcommit?branch=main)\n[![Maintainability](https://api.codeclimate.com/v1/badges/5da42f7f365e5fef6b4c/maintainability)](https://codeclimate.com/github/sds/overcommit/maintainability)\n[![Inline docs](http://inch-ci.org/github/sds/overcommit.svg?branch=main)](http://inch-ci.org/github/sds/overcommit)\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/sds/overcommit/master/logo/horizontal.png\" width=\"65%\" alt=\"Overcommit Logo\"/>\n</p>\n\n`overcommit` is a tool to manage and configure\n[Git hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks).\n\nIn addition to supporting a wide variety of hooks that can be used across\nmultiple repositories, you can also define hooks specific to a repository which\nare stored in source control. You can also easily\n[add your existing hook scripts](#adding-existing-git-hooks) without writing\nany Ruby code.\n\n- [Requirements](#requirements)\n  - [Windows](#windows)\n  - [Dependencies](#dependencies)\n- [Installation](#installation)\n  - [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)\n- [Usage](#usage)\n  - [Skipping Hooks](#skipping-hooks)\n  - [Disabling Overcommit](#disabling-overcommit)\n  - [Disabling Colorized Output](#disabling-colorized-output)\n- [Continuous Integration](#continuous-integration)\n- [Configuration](#configuration)\n  - [Hook Options](#hook-options)\n  - [Hook Categories](#hook-categories)\n    - [The `ALL` Hook](#the-all-hook)\n  - [Gemfile](#gemfile)\n  - [Plugin Directory](#plugin-directory)\n  - [Quiet Hook Runs](#quiet-hook-runs)\n  - [Concurrency](#concurrency)\n  - [Signature Verification](#signature-verification)\n- [Built-In Hooks](#built-in-hooks)\n  - [CommitMsg](#commitmsg)\n  - [PostCheckout](#postcheckout)\n  - [PostCommit](#postcommit)\n  - [PostMerge](#postmerge)\n  - [PostRewrite](#postrewrite)\n  - [PreCommit](#precommit)\n    - [WARNING: pre-commit hooks cannot have side effects](#warning-pre-commit-hooks-cannot-have-side-effects)\n  - [PrePush](#prepush)\n  - [PreRebase](#prerebase)\n- [Repo-Specific hooks](#repo-specific-hooks)\n  - [Adding Existing Git Hooks](#adding-existing-git-hooks)\n- [Security](#security)\n  - [Disabling Signature Checking](#disabling-signature-checking)\n- [Contributing](#contributing)\n- [Community](#community)\n- [Changelog](#changelog)\n- [License](#license)\n\n## Requirements\n\nThis project aims to support the following Ruby runtimes on \\*nix (and best effort on Windows):\n\n* Ruby 2.6+\n\n### Dependencies\n\nSome hooks have third-party dependencies. For example, to lint your\n[SCSS](http://sass-lang.com/) files, you're going to need the\n[scss_lint gem](https://github.com/sds/scss-lint).\n\nDepending on the hooks you enable/disable for your repository, you'll need to\nensure your development environment already has those dependencies installed.\nMost hooks will display a warning if a required executable isn't available.\n\nIf you are using Bundler to manage your Ruby gem dependencies, you'll likely\nwant to use the [`gemfile`](#gemfile) option to control which gem versions are\navailable during your hook runs.\n\n## Installation\n\n`overcommit` is installed via [RubyGems](https://rubygems.org/). It is strongly\nrecommended that your environment support running `gem install` without\nrequiring root user privileges via `sudo` or otherwise. Using a Ruby version\nmanager like [`rbenv`](https://github.com/rbenv/rbenv/) or\n[`rvm`](https://rvm.io/) is recommended.\n\nOnce you have an environment that allows you to install gems without `sudo`,\nrun:\n\n```bash\ngem install overcommit\n```\n\nYou can then run the `overcommit` command to install hooks into repositories.\n\n```bash\nmkdir important-project\ncd important-project\ngit init\novercommit --install\n```\n\nAfter running `overcommit --install`, any existing hooks for your repository\nwhich Overcommit will replace will be backed up. You can restore everything to\nthe way it was by running `overcommit --uninstall`.\n\n### Automatically Install Overcommit Hooks\n\nIf you want to use `overcommit` for all repositories you create/clone going\nforward, add the following to automatically run in your shell environment:\n\n```bash\nexport GIT_TEMPLATE_DIR=\"$(overcommit --template-dir)\"\n```\n\nThe `GIT_TEMPLATE_DIR` provides a directory for Git to use as a template\nfor automatically populating the `.git` directory. If you have your own\ntemplate directory, you might just want to copy the contents of\n`overcommit --template-dir` to that directory.\n\n## Usage\n\nOnce you've installed the hooks via `overcommit --install`, they will\nautomatically run when the appropriate hook is triggered.\n\nThe `overcommit` executable supports the following command-line flags:\n\nCommand Line Flag         | Description\n--------------------------|----------------------------------------------------\n`-i`/`--install`          | Install Overcommit hooks in a repository\n`-u`/`--uninstall`        | Remove Overcommit hooks from a repository\n`-f`/`--force`            | Don't bail on install if other hooks already exist--overwrite them\n`-l`/`--list-hooks`       | Display all available hooks in the current repository\n`-r`/`--run`              | Run pre-commit hook against all tracked files in repository\n`--diff <ref>`            | Run pre-commit hook against all changed files relative to `<ref>`\n`-t`/`--template-dir`     | Print location of template directory\n`-h`/`--help`             | Show command-line flag documentation\n`-v`/`--version`          | Show version\n\n### Skipping Hooks\n\nSometimes a hook will report an error that for one reason or another you'll want\nto ignore. To prevent these errors from blocking your commit, you can include\nthe name of the relevant hook in the `SKIP` environment variable, e.g.\n\n```bash\nSKIP=RuboCop git commit\n```\n\nIf you would prefer to specify a whitelist of hooks rather than a blacklist, use\nthe `ONLY` environment variable instead.\n\n```bash\nONLY=RuboCop git commit\n```\n\nUse this feature sparingly, as there is no point to having the hook in the first\nplace if you're just going to ignore it. If you want to ensure a hook is never\nskipped, set the `required` option to `true` in its configuration. If you\nattempt to skip it, you'll see a warning telling you that the hook is required,\nand the hook will still run.\n\n### Disabling Overcommit\n\nIf you have scripts that execute `git` commands where you don't want Overcommit\nhooks to run, you can disable Overcommit entirely by setting the\n`OVERCOMMIT_DISABLE` environment variable.\n\n```bash\nOVERCOMMIT_DISABLE=1 ./my-custom-script\n```\n\n### Disabling Colorized Output\n\nOvercommit automatically colorizes its output based on whether it is outputting\nto a TTY. However, you can manually enable/disable color by setting the\n`OVERCOMMIT_COLOR` environment variable.\n\n```bash\nOVERCOMMIT_COLOR=0 git commit\n```\n\n## Continuous Integration\n\nYou can run the same set of hooks that would be executed in a pre-commit hook\nagainst your entire repository by running `overcommit --run`. This makes it\neasy to have the checks verified by a CI service such as\n[Travis CI](https://travis-ci.com/), including custom hooks you've written\nyourself.\n\nThe `--run` flag works by creating a pre-commit context that assumes _all_ the\nfiles in your repository have changed, and follows the same rules as a normal\npre-commit check. If any hook fails with an error, it will return a non-zero\nexit code.\n\n## Configuration\n\nOvercommit provides a flexible configuration system that allows you to tailor\nthe built-in hooks to suit your workflow. All configuration specific to a\nrepository is stored in `.overcommit.yml` in the top-level directory of the\nrepository.\n\nWhen writing your own configuration, it will automatically extend the\n[default configuration](config/default.yml), so you only need to specify\nyour configuration with respect to the default. In order to\nenable/disable hooks, you can add the following to your repo-specific\nconfiguration file:\n\n```yaml\nPreCommit:\n  RuboCop:\n    enabled: true\n    command: ['bundle', 'exec', 'rubocop'] # Invoke within Bundler context\n```\n\nAdditionally, you may wish to have repo-specific configurations that are local to your computer that are not part of the shared repo config.\nAdding a `.local-overcommit.yml` file in the top-level directory of the repository adds another configuration file. This file works the same as `.overcommit.yml`.\nAdding this to ignored files in a git repo will allow you to have a local configuration per repo.\n\n### Hook Options\n\nIndividual hooks expose both built-in configuration options as well as their\nown custom options unique to each hook. The following table lists all built-in\nconfiguration options:\n\nOption                                  | Description\n----------------------------------------|--------------------------------------\n`enabled`                               | If `false`, this hook will never be run\n`required`                              | If `true`, this hook cannot be skipped via the `SKIP` environment variable\n`quiet`                                 | If `true`, this hook does not display any output unless it warns/fails\n`description`                           | Message displayed while hook is running.\n`requires_files`                        | If `true`, this hook runs only if files that are applicable to it have been modified. See `include` and `exclude` for how to specify applicable files.\n`include`                               | File paths or glob patterns of files that apply to this hook. The hook will only run on the applicable files when they have been modified. Note that the concept of modified varies for different types of hooks. By default, `include` matches every file until you specify a list of patterns.\n`exclude`                               | File paths or glob patterns of files that do not apply to this hook. This is used to exclude any files that would have been matched by `include`.\n`exclude_branches`                      | List of branch names or glob patterns of branches that this hook should not run against.\n`exclude_remotes`                       | *`PrePush` hooks only.* List of remote names that the hook should not run against.\n`include_remote_ref_deletions`          | *`PrePush` hooks only.* By default, `PrePush` hooks will **not** run for pushes that delete a remote ref (i.e. branches or tags). Set to `true` to have the hook run even for deleted remote ref.\n`problem_on_unmodified_line`            | How to treat errors reported on lines that weren't modified during the action captured by this hook (e.g. for pre-commit hooks, warnings/errors reported on lines that were not staged with `git add` may not be warnings/errors you care about). Valid values are `report`: report errors/warnings as-is regardless of line location (default); `warn`: report errors as warnings if they are on lines you didn't modify; and `ignore`: don't display errors/warnings at all if they are on lines you didn't modify (`ignore` is _not_ recommended).\n`on_fail`                               | Change the status of a failed hook to `warn` or `pass`. This allows you to treat failures as warnings or potentially ignore them entirely, but you should use caution when doing so as you might be hiding important information.\n`on_warn`                               | Similar to `on_fail`, change the status of a hook that returns a warning status to either `pass` (you wish to silence warnings entirely) or `fail` (you wish to treat all warnings as errors).\n`required_executable`                   | Name of an executable that must exist in order for the hook to run. If this is a path (e.g. `./bin/ruby`), ensures that the executable file exists at the given location relative to the repository root. Otherwise, if it just the name of an executable (e.g. `ruby`) checks if the executable can be found in one of the directories in the `PATH` environment variable. Set this to a specific path if you want to always use an executable that is stored in your repository. (e.g. RubyGems bin stubs, Node.js binaries, etc.)\n`required_library`/`required_libraries` | List of Ruby libraries to load with `Kernel.require` before the hook runs. This is specifically for hooks that integrate with external Ruby libraries.\n`command`                               | Array of arguments to use as the command. How each hook uses this is different, but it allows hooks to change the context with which they run. For example, you can change the command to be `['bundle', 'exec', 'rubocop']` instead of just `rubocop` so that you can use the gem versions specified in your local `Gemfile.lock`. This defaults to the name of the `required_executable`.\n`flags`                                 | Array of arguments to append to the `command`. This is useful for customizing the behavior of a tool. It's also useful when a newer version of a tool removes/renames existing flags, so you can update the flags via your `.overcommit.yml` instead of waiting for an upstream fix in Overcommit.\n`env`                                   | Hash of environment variables the hook should be run with. This is intended to be used as a last resort when an executable a hook runs is configured only via an environment variable. Any pre-existing environment variables with the same names as ones defined in `env` will have their original values restored after the hook runs. **NOTE:** Currently, only strings are accepted values. Boolean values will raise an error. **WARNING**: If you set the same environment variable for multiple hooks and you've enabled parallel hook runs, since the environment is shared across all threads you could accidentally have these separate hooks trample on each other. In this case, you should disable parallelization for the hook using the `parallelize` option.\n`parallelize`                           | Whether to allow this hook to be run concurrently with other hooks. Disable this if the hook requires access to a shared resource that other hooks may also access and modify (e.g. files, the git index, process environment variables, etc).\n`processors`                            | The number of processing units to reserve for this hook. This does not reserve CPUs, but indicates that out of the total number of possible concurrent hooks allowed by the global `concurrency` option, this hook requires the specified number. Thus in the typical case where `concurrency` is set to the number of available cores (default), and you have a hook that executes an application which itself creates 2 threads (or is otherwise scheduled on 2 cores), you can indicate that Overcommit should allocate 2 `processors` to the hook. Ideally this means your hooks won't put undue load on your available cores.\n`install_command`                       | Command the user can run to install the `required_executable` (or alternately the specified `required_libraries`). This is intended for documentation purposes, as Overcommit does not install software on your behalf since there are too many edge cases where such behavior would result in incorrectly configured installations (e.g. installing a Python package in the global package space instead of in a virtual environment).\n`skip_file_checkout`                    | Whether to skip this hook for file checkouts (e.g. `git checkout some-ref -- file`). Only applicable to `PostCheckout` hooks.\n`skip_if`                               | Array of arguments to be executed to determine whether or not the hook should run. For example, setting this to a value of `['bash', '-c', '! which my-executable']` would allow you to skip running this hook if `my-executable` was not in the bin path.\n\nIn addition to the built-in configuration options, each hook can expose its\nown unique configuration options. The `AuthorEmail` hook, for example, allows\nyou to customize the regex used to check commit author emails via the `pattern`\noption&mdash;useful if you want to enforce that developers use a company\nemail address for their commits. This provides incredible flexibility for hook\nauthors as you can make your hooks sufficiently generic and then customize them\non a per-project basis.\n\n### Hook Categories\n\nHook configurations are organized into categories based on the type of hook. So\n`pre-commit` hooks are located under the `PreCommit` option, and `post-commit`\nhooks are located under `PostCommit`. See the\n[default configuration](config/default.yml) for a thorough example.\n\n#### The `ALL` Hook\n\nWithin a hook category, there is a special type of hook configuration that\napplies to _all_ hooks in the category. This configuration looks like a normal\nhook configuration, except it has the name `ALL`:\n\n```yaml\nPreCommit:\n  ALL:\n    problem_on_unmodified_line: warn\n    requires_files: true\n    required: false\n    quiet: false\n\n  SomeHook:\n    enabled: true\n\n  ...\n```\n\nThe `ALL` configuration is useful for when you want to\n[DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) up your\nconfiguration, or when you want to apply changes across an entire category of\nhooks.\n\nNote that array configuration options (like `include`/`exclude`) in the\nspecial `ALL` hook section are not merged with individual hook configurations\nif custom ones are defined for the hook.\nAny custom configuration option for `include`/`exclude` will replace the `ALL`\nhook's configuration. If you want to have a global list of default exclusions\nand extend them with a custom list, you can use YAML references, e.g.\n\n```yaml\nPreCommit:\n  ALL:\n    exclude: &default_excludes\n      - 'node_modules/**/*'\n      - 'vendor/**/*'\n  MyHook:\n    exclude:\n      - *default_excludes\n      - 'another/directory/in/addition/to/default/excludes/**/*'\n```\n\nAgain, you can consult the [default configuration](config/default.yml) for\ndetailed examples of how the `ALL` hook can be used.\n\n### Gemfile\n\nYou may want to enforce the version of Overcommit or other gems that you use in\nyour git hooks. This can be done by specifying the `gemfile` option in your\n`.overcommit.yml`.\n\nThe `gemfile` option tells Overcommit to load the specified file with\n[Bundler](http://bundler.io/), the standard gem dependency manager for Ruby.\nThis is useful if you would like to:\n\n  - Enforce a specific version of Overcommit to use for all hook runs\n    (or to use a version from the master branch that has not been released yet)\n  - Enforce a specific version or unreleased branch is used for a gem you want\n    to use in your git hooks\n\nLoading a Bundler context necessarily adds a startup delay to your hook runs\nas Bundler parses the specified `Gemfile` and checks that the dependencies are\nsatisfied. Thus for projects with many gems this can introduce a noticeable\ndelay.\n\nThe recommended workaround is to create a separate `Gemfile` in the root of\nyour repository (call it `.overcommit_gems.rb`), and include only the gems that\nyour Overcommit hooks need in order to run. Generate the associated lock file\nby running:\n\n```bash\nbundle install --gemfile=.overcommit_gems.rb\n```\n\n...and commit `.overcommit_gems.rb` and the resulting\n`.overcommit_gems.rb.lock` file to your repository. Set your `gemfile` option\nto `.overcommit_gems.rb`, and you're all set.\n\nUsing a smaller Gemfile containing only the gems used by your Overcommit hooks\nsignificantly reduces the startup delay in your hook runs. It is thus the\nrecommended approach unless your project has a relatively small number of gems\nin your `Gemfile`.\n\n### Plugin Directory\n\nYou can change the directory that project-specific hooks are loaded from via\nthe `plugin_directory` option. The default directory is `.git-hooks`.\n\n### Quiet Hook Runs\n\nIf you prefer to have your hooks be completely silent unless there is a\nproblem, you can set the top-level `quiet` option to `true`. Note that if you\nhave many hooks or slow hooks this may not be desirable, as you don't get\nvisual feedback indicating the general progress of the hook run.\n\n### Concurrency\n\nOvercommit runs hooks in parallel by default, with a number of concurrent\nworkers equal to the number of logical cores on your machine. If you know your\nparticular set of hooks would benefit from higher/lower number of workers, you\ncan adjust the global `concurrency` option. You can define single-operator\nmathematical expressions, e.g. `%{processors} * 2`, or `%{processors} / 2`.\n\n```yaml\nconcurrency: '%{processors} / 4'\n```\n\nNote that individual hooks can specify the number of processors they require\nwith the `processors` hook option. See the [hook options](#hook-options)\nsection for more details.\n\n### Signature Verification\n\nYou can disable manual verification of signatures by setting\n`verify_signatures` to `false`. See the [Security](#security) section for more\ninformation on this option and what exactly it controls.\n\n## Built-In Hooks\n\nCurrently, Overcommit supports the following hooks out of the box&mdash;simply\nenable them in your `.overcommit.yml`.\n\n**Note**: Hooks with a `*` are enabled by default.\n\n**Warning**: This list represents the list of hooks available on the `master`\nbranch. Please consult the [change log](CHANGELOG.md) to view which hooks have\nnot been released yet.\n\n### CommitMsg\n\n`commit-msg` hooks are run against every commit message you write before a\ncommit is created. A failed hook prevents a commit from being created. These\nhooks are useful for enforcing policies on your commit messages, e.g. ensuring\na task ID is included for tracking purposes, or ensuring your commit messages\nfollow [proper formatting guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).\n\n* [`*`CapitalizedSubject](lib/overcommit/hook/commit_msg/capitalized_subject.rb)\n* [`*`EmptyMessage](lib/overcommit/hook/commit_msg/empty_message.rb)\n* [GerritChangeId](lib/overcommit/hook/commit_msg/gerrit_change_id.rb)\n* [HardTabs](lib/overcommit/hook/commit_msg/hard_tabs.rb)\n* [MessageFormat](lib/overcommit/hook/commit_msg/message_format.rb)\n* [RussianNovel](lib/overcommit/hook/commit_msg/russian_novel.rb)\n* [`*`SingleLineSubject](lib/overcommit/hook/commit_msg/single_line_subject.rb)\n* [SpellCheck](lib/overcommit/hook/commit_msg/spell_check.rb)\n* [`*`TextWidth](lib/overcommit/hook/commit_msg/text_width.rb)\n* [`*`TrailingPeriod](lib/overcommit/hook/commit_msg/trailing_period.rb)\n\n### PostCheckout\n\n`post-checkout` hooks run after a successful `git checkout`, or in other words\nany time your `HEAD` changes or a file is explicitly checked out.\n\n* [BowerInstall](lib/overcommit/hook/post_checkout/bower_install.rb)\n* [BundleInstall](lib/overcommit/hook/post_checkout/bundle_install.rb)\n* [ComposerInstall](lib/overcommit/hook/post_checkout/composer_install.rb)\n* [IndexTags](lib/overcommit/hook/post_checkout/index_tags.rb)\n* [NpmInstall](lib/overcommit/hook/post_checkout/npm_install.rb)\n* [SubmoduleStatus](lib/overcommit/hook/post_checkout/submodule_status.rb)\n* [YarnInstall](lib/overcommit/hook/post_checkout/yarn_install.rb)\n\n### PostCommit\n\n`post-commit` hooks run after a commit is successfully created. A hook failing\nin this case does not prevent the commit since it has already occurred;\nhowever, it can be used to alert the user to some issue.\n\n* [BowerInstall](lib/overcommit/hook/post_commit/bower_install.rb)\n* [BundleInstall](lib/overcommit/hook/post_commit/bundle_install.rb)\n* [Commitplease](lib/overcommit/hook/post_commit/commitplease.rb)\n* [ComposerInstall](lib/overcommit/hook/post_commit/composer_install.rb)\n* [GitGuilt](lib/overcommit/hook/post_commit/git_guilt.rb)\n* [IndexTags](lib/overcommit/hook/post_commit/index_tags.rb)\n* [NpmInstall](lib/overcommit/hook/post_commit/npm_install.rb)\n* [SubmoduleStatus](lib/overcommit/hook/post_commit/submodule_status.rb)\n* [YarnInstall](lib/overcommit/hook/post_commit/yarn_install.rb)\n\n### PostMerge\n\n`post-merge` hooks run after a `git merge` executes successfully with no merge\nconflicts. A hook failing in this case does not prevent the merge since it has\nalready occurred; however, it can be used to alert the user to some issue.\n\n* [BowerInstall](lib/overcommit/hook/post_merge/bower_install.rb)\n* [BundleInstall](lib/overcommit/hook/post_merge/bundle_install.rb)\n* [ComposerInstall](lib/overcommit/hook/post_merge/composer_install.rb)\n* [IndexTags](lib/overcommit/hook/post_merge/index_tags.rb)\n* [NpmInstall](lib/overcommit/hook/post_merge/npm_install.rb)\n* [SubmoduleStatus](lib/overcommit/hook/post_merge/submodule_status.rb)\n* [YarnInstall](lib/overcommit/hook/post_merge/yarn_install.rb)\n\n### PostRewrite\n\n`post-rewrite` hooks run after a commit is modified by a `git commit --amend`\nor `git rebase`. A hook failing in this case does not prevent the rewrite since\nit has already occurred; however, it can be used to alert the user to some\nissue.\n\n* [BowerInstall](lib/overcommit/hook/post_rewrite/bower_install.rb)\n* [BundleInstall](lib/overcommit/hook/post_rewrite/bundle_install.rb)\n* [ComposerInstall](lib/overcommit/hook/post_rewrite/composer_install.rb)\n* [IndexTags](lib/overcommit/hook/post_rewrite/index_tags.rb)\n* [NpmInstall](lib/overcommit/hook/post_rewrite/npm_install.rb)\n* [SubmoduleStatus](lib/overcommit/hook/post_rewrite/submodule_status.rb)\n* [YarnInstall](lib/overcommit/hook/post_rewrite/yarn_install.rb)\n\n### PreCommit\n\n`pre-commit` hooks are run after `git commit` is executed, but before the\ncommit message editor is displayed. If a hook fails, the commit will not be\ncreated. These hooks are ideal for syntax checkers, linters, and other checks\nthat you want to run before you allow a commit to even be created.\n\n#### WARNING: pre-commit hooks cannot have side effects\n\n`pre-commit` hooks currently do not support hooks with side effects (such as\nmodifying files and adding them to the index with `git add`). This is a\nconsequence of Overcommit's pre-commit hook stashing behavior to ensure hooks\nare run against _only the changes you are about to commit_.\n\nWithout Overcommit, the proper way to write a `pre-commit` hook would be to\nextract the staged changes into temporary files and lint those files\ninstead of whatever contents are in your working tree (as you don't want\nunstaged changes to taint your results). Overcommit takes care\nof this for you, but to do it in a generalized way introduces this\nlimitation. See the [thread tracking this\nissue](https://github.com/sds/overcommit/issues/238) for more details.\n\n* [`*`AuthorEmail](lib/overcommit/hook/pre_commit/author_email.rb)\n* [`*`AuthorName](lib/overcommit/hook/pre_commit/author_name.rb)\n* [BerksfileCheck](lib/overcommit/hook/pre_commit/berksfile_check.rb)\n* [`*`BrokenSymlinks](lib/overcommit/hook/pre_commit/broken_symlinks.rb)\n* [BundleAudit](lib/overcommit/hook/pre_commit/bundle_audit.rb)\n* [BundleCheck](lib/overcommit/hook/pre_commit/bundle_check.rb)\n* [BundleOutdated](lib/overcommit/hook/pre_commit/bundle_outdated.rb)\n* [`*`CaseConflicts](lib/overcommit/hook/pre_commit/case_conflicts.rb)\n* [ChamberSecurity](lib/overcommit/hook/pre_commit/chamber_security.rb)\n* [CodeSpellCheck](lib/overcommit/hook/pre_commit/code_spell_check.rb)\n* [CoffeeLint](lib/overcommit/hook/pre_commit/coffee_lint.rb)\n* [Credo](lib/overcommit/hook/pre_commit/credo.rb)\n* [CssLint](lib/overcommit/hook/pre_commit/css_lint.rb)\n* [DartAnalyzer](lib/overcommit/hook/pre_commit/dart_analyzer.rb)\n* [Dogma](lib/overcommit/hook/pre_commit/dogma.rb)\n* [ErbLint](lib/overcommit/hook/pre_commit/erb_lint.rb)\n* [EsLint](lib/overcommit/hook/pre_commit/es_lint.rb)\n* [ExecutePermissions](lib/overcommit/hook/pre_commit/execute_permissions.rb)\n* [Fasterer](lib/overcommit/hook/pre_commit/fasterer.rb)\n* [FileSize](lib/overcommit/hook/pre_commit/file_size.rb)\n* [FixMe](lib/overcommit/hook/pre_commit/fix_me.rb)\n* [Flay](lib/overcommit/hook/pre_commit/flay.rb)\n* [Foodcritic](lib/overcommit/hook/pre_commit/foodcritic.rb)\n* [ForbiddenBranches](lib/overcommit/hook/pre_commit/forbidden_branches.rb)\n* [GoLint](lib/overcommit/hook/pre_commit/go_lint.rb)\n* [GoVet](lib/overcommit/hook/pre_commit/go_vet.rb)\n* [Hadolint](lib/overcommit/hook/pre_commit/hadolint.rb)\n* [LicenseFinder](lib/overcommit/hook/pre_commit/license_finder.rb)\n* [HamlLint](lib/overcommit/hook/pre_commit/haml_lint.rb)\n* [HardTabs](lib/overcommit/hook/pre_commit/hard_tabs.rb)\n* [Hlint](lib/overcommit/hook/pre_commit/hlint.rb)\n* [HtmlHint](lib/overcommit/hook/pre_commit/html_hint.rb)\n* [HtmlTidy](lib/overcommit/hook/pre_commit/html_tidy.rb)\n* [ImageOptim](lib/overcommit/hook/pre_commit/image_optim.rb)\n* [JavaCheckstyle](lib/overcommit/hook/pre_commit/java_checkstyle.rb)\n* [Jscs](lib/overcommit/hook/pre_commit/jscs.rb)\n* [JsHint](lib/overcommit/hook/pre_commit/js_hint.rb)\n* [JsLint](lib/overcommit/hook/pre_commit/js_lint.rb)\n* [Jsl](lib/overcommit/hook/pre_commit/jsl.rb)\n* [JsonSyntax](lib/overcommit/hook/pre_commit/json_syntax.rb)\n* [KtLint](lib/overcommit/hook/pre_commit/kt_lint.rb)\n* [LicenseHeader](lib/overcommit/hook/pre_commit/license_header.rb)\n* [LineEndings](lib/overcommit/hook/pre_commit/line_endings.rb)\n* [LocalPathsInGemfile](lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb)\n* [Mdl](lib/overcommit/hook/pre_commit/mdl.rb)\n* [`*`MergeConflicts](lib/overcommit/hook/pre_commit/merge_conflicts.rb)\n* [NginxTest](lib/overcommit/hook/pre_commit/nginx_test.rb)\n* [PhpCs](lib/overcommit/hook/pre_commit/php_cs.rb)\n* [PhpCsFixer](lib/overcommit/hook/pre_commit/php_cs_fixer.rb)\n* [PhpLint](lib/overcommit/hook/pre_commit/php_lint.rb)\n* [PhpStan](lib/overcommit/hook/pre_commit/php_stan.rb)\n* [Pronto](lib/overcommit/hook/pre_commit/pronto.rb)\n* [PuppetLint](lib/overcommit/hook/pre_commit/puppet_lint.rb)\n* [PuppetMetadataJsonLint](lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb)\n* [Pycodestyle](lib/overcommit/hook/pre_commit/pycodestyle.rb)\n* [Pydocstyle](lib/overcommit/hook/pre_commit/pydocstyle.rb)\n* [Pyflakes](lib/overcommit/hook/pre_commit/pyflakes.rb)\n* [Pylint](lib/overcommit/hook/pre_commit/pylint.rb)\n* [PythonFlake8](lib/overcommit/hook/pre_commit/python_flake8.rb)\n* [RakeTarget](lib/overcommit/hook/pre_commit/rake_target.rb)\n* [RailsBestPractices](lib/overcommit/hook/pre_commit/rails_best_practices.rb)\n* [RailsSchemaUpToDate](lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb)\n* [Reek](lib/overcommit/hook/pre_commit/reek.rb)\n* [RuboCop](lib/overcommit/hook/pre_commit/rubo_cop.rb)\n* [RubyLint](lib/overcommit/hook/pre_commit/ruby_lint.rb)\n* [RubySyntax](lib/overcommit/hook/pre_commit/ruby_syntax.rb)\n* [SwiftLint](lib/overcommit/hook/pre_commit/swift_lint.rb)\n* [Scalariform](lib/overcommit/hook/pre_commit/scalariform.rb)\n* [Scalastyle](lib/overcommit/hook/pre_commit/scalastyle.rb)\n* [ScssLint](lib/overcommit/hook/pre_commit/scss_lint.rb)\n* [SemiStandard](lib/overcommit/hook/pre_commit/semi_standard.rb)\n* [ShellCheck](lib/overcommit/hook/pre_commit/shell_check.rb)\n* [SlimLint](lib/overcommit/hook/pre_commit/slim_lint.rb)\n* [Sorbet](lib/overcommit/hook/pre_commit/sorbet.rb)\n* [Sqlint](lib/overcommit/hook/pre_commit/sqlint.rb)\n* [Standard](lib/overcommit/hook/pre_commit/standard.rb)\n* [Stylelint](lib/overcommit/hook/pre_commit/stylelint.rb)\n* [TrailingWhitespace](lib/overcommit/hook/pre_commit/trailing_whitespace.rb)\n* [TravisLint](lib/overcommit/hook/pre_commit/travis_lint.rb)\n* [TsLint](lib/overcommit/hook/pre_commit/ts_lint.rb)\n* [Vint](lib/overcommit/hook/pre_commit/vint.rb)\n* [W3cCss](lib/overcommit/hook/pre_commit/w3c_css.rb)\n* [W3cHtml](lib/overcommit/hook/pre_commit/w3c_html.rb)\n* [XmlLint](lib/overcommit/hook/pre_commit/xml_lint.rb)\n* [XmlSyntax](lib/overcommit/hook/pre_commit/xml_syntax.rb)\n* [YamlLint](lib/overcommit/hook/pre_commit/yaml_lint.rb)\n* [YamlSyntax](lib/overcommit/hook/pre_commit/yaml_syntax.rb)\n* [YardCoverage](lib/overcommit/hook/pre_commit/yard_coverage.rb)\n* [YarnCheck](lib/overcommit/hook/pre_commit/yarn_check.rb)\n\n### PrePush\n\n`pre-push` hooks are run during `git push`, after remote refs have been updated\nbut before any objects have been transferred. If a hook fails, the push is\naborted.\n\n* [Brakeman](lib/overcommit/hook/pre_push/brakeman.rb)\n* [FlutterTest](lib/overcommit/hook/pre_push/flutter_test.rb)\n* [Minitest](lib/overcommit/hook/pre_push/minitest.rb)\n* [PhpUnit](lib/overcommit/hook/pre_push/php_unit.rb)\n* [Pronto](lib/overcommit/hook/pre_push/pronto.rb)\n* [ProtectedBranches](lib/overcommit/hook/pre_push/protected_branches.rb)\n* [PubTest](lib/overcommit/hook/pre_push/pub_test.rb)\n* [Pytest](lib/overcommit/hook/pre_push/pytest.rb)\n* [PythonNose](lib/overcommit/hook/pre_push/python_nose.rb)\n* [RakeTarget](lib/overcommit/hook/pre_push/rake_target.rb)\n* [RSpec](lib/overcommit/hook/pre_push/r_spec.rb)\n* [TestUnit](lib/overcommit/hook/pre_push/test_unit.rb)\n\n### PreRebase\n\n`pre-rebase` hooks are run during `git rebase`, before any commits are rebased.\nIf a hook fails, the rebase is aborted.\n\n* [MergedCommits](lib/overcommit/hook/pre_rebase/merged_commits.rb)\n\n## Repo-Specific hooks\n\nOut of the box, `overcommit` comes with a set of hooks that enforce a variety of\nstyles and lints. However, some hooks only make sense in the context of a\nspecific repository.\n\nFor example, you can have a number of simple checks that run\nagainst your code to catch common errors. For example, if you use\n[RSpec](http://rspec.info/), you can make sure all spec files contain the\nline `require 'spec_helper'`.\n\nInside our repository, we can add the file\n`.git-hooks/pre_commit/ensure_spec_helper.rb` in order to automatically check\nour spec files:\n\n```ruby\nmodule Overcommit::Hook::PreCommit\n  class EnsureSpecHelper < Base\n    def run\n      errors = []\n\n      applicable_files.each do |file|\n        if File.read(file) !~ /^require 'spec_helper'/\n          errors << \"#{file}: missing `require 'spec_helper'`\"\n        end\n      end\n\n      return :fail, errors.join(\"\\n\") if errors.any?\n\n      :pass\n    end\n  end\nend\n```\n\nThe corresponding configuration for this hook would look like:\n\n```yaml\nPreCommit:\n  EnsureSpecHelper:\n    enabled: true\n    description: 'Checking for missing inclusion of spec_helper'\n    include: '**/*_spec.rb'\n```\n\n### Adding Existing Git Hooks\n\nYou might already have hook scripts written which you'd like to integrate with\nOvercommit right away. To make this easy, Overcommit allows you to include\nyour hook script in your configuration without writing any Ruby code.\nFor example:\n\n```yaml\nPostCheckout:\n  CustomScript:\n    enabled: true\n    required_executable: './bin/custom-script'\n```\n\nSo long as a command is given (either by specifying the `command` option\ndirectly or specifying `required_executable`) a special hook is created that\nexecutes the command and appends any arguments and standard input stream that\nwould have been passed to the regular hook. The hook passes or fails based\non the exit status of the command.\n\nThe script is executed as if Git were calling the hook directly. If you want\nto understand which arguments are passed to the script depending on the type\nof hook, see the [git-hooks documentation][GHD].\n\n[GHD]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks\n\n## Security\n\nWhile Overcommit can make managing Git hooks easier and more convenient,\nthis convenience can come at a cost of being less secure.\n\nSince installing Overcommit hooks will allow arbitrary plugin code in your\nrepository to be executed, you expose yourself to an attack where checking\nout code from a third party can result in malicious code being executed\non your system.\n\nAs an example, consider the situation where you have an open source project.\nAn attacker could submit a pull request which adds a `post-checkout` hook\nthat executes some malicious code. When you fetch and checkout this pull\nrequest, the `post-checkout` hook will be run on your machine, along with\nthe malicious code that you just checked out.\n\nOvercommit attempts to address this problem by storing a signature of your\nconfiguration and all hook plugin code since the last time it ran. When the\nsignature changes, a warning is displayed alerting you to which plugins have\nchanged. It is then up to you to manually verify that the changes are not\nmalicious, and then continue running the hooks.\n\nThe signature is derived from the contents of the plugin's source code itself\nand any configuration for the plugin. Thus a change to the plugin's source\ncode or your local repo's `.overcommit.yml` file could result in a signature\nchange.\n\n### Disabling Signature Checking\n\nIn typical usage, your plugins usually don't change too often, so this warning\nshouldn't become a nuisance. However, users who work within proprietary\nrepositories where all developers who can push changes to the repository\nalready have a minimum security clearance may wish to disable this check.\n\nWhile not recommended, you can disable signature verification by setting\n`verify_signatures` to `false` in your `.overcommit.yml` file.\n\n**Regardless of whether you have `verify_signatures` disabled for your project,\nif you are running Overcommit for the first time you will need to sign your\nconfiguration with `overcommit --sign`**. This needs to happen once so\nOvercommit can record in your local git repo's configuration (outside of source\ncontrol) that you intend to enable/disable verification. This way if someone\nelse changes `verify_signatures` you'll be asked to confirm the change.\n\n## Contributing\n\nWe love contributions to Overcommit, be they bug reports, feature ideas, or\npull requests. See our [guidelines for contributing](CONTRIBUTING.md) to best\nensure your thoughts, ideas, or code get merged.\n\n## Community\n\nAll major discussion surrounding Overcommit happens on the\n[GitHub issues list](https://github.com/sds/overcommit/issues).\n\n## Changelog\n\nIf you're interested in seeing the changes and bug fixes between each version\nof `overcommit`, read the [Overcommit Changelog](CHANGELOG.md).\n\n## License\n\nThis project is released under the [MIT license](MIT-LICENSE).\n\nThe Overcommit logo is adapted from the [Git Logo by Jason Long][GL], and\nis licensed under the [Creative Commons Attribution 3.0 Unported License][CC3].\n\n[GL]: https://git-scm.com/downloads/logos\n[CC3]: http://creativecommons.org/licenses/by/3.0/\n"
  },
  {
    "path": "bin/overcommit",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nrequire 'yaml'\nif gemfile = YAML.load_file('.overcommit.yml')['gemfile'] rescue nil\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    # We need to temporarily silence STDERR to remove annoying Gem specification\n    # warnings that ultimately don't matter, e.g.\n    # https://github.com/rubygems/rubygems/issues/1070\n    old_stderr = $stderr\n    begin\n      $stderr = File.new(File::NULL, 'w')\n      Bundler.setup\n    ensure\n      $stderr = old_stderr\n    end\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  rescue Gem::LoadError => e\n    # Handle case where user is executing overcommit without `bundle exec` and\n    # whose local Gemfile has a gem requirement that does not match a gem\n    # requirement of the installed version of Overcommit.\n    raise unless e.message =~ /already activated/i\n\n    exec('bundle', 'exec', $0, *ARGV)\n  end\nend\n\nbegin\n  require 'overcommit/cli'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    raise\n  end\n\n  exit 64 # EX_USAGE\nend\n\nlogger = Overcommit::Logger.new(STDOUT)\n\nOvercommit::CLI.new(ARGV, STDIN, logger).run\n"
  },
  {
    "path": "config/default.yml",
    "content": "# Default configuration that all Overcommit configurations inherit from.\n#\n# This is an opinionated list of which hooks are valuable to run and what their\n# out-of-the-box settings should be.\n#-------------------------------------------------------------------------------\n\n# Loads Bundler context from a Gemfile. If false, does nothing (default).\n#\n# Specifying a Gemfile for Bundler to load allows you to control which gems are\n# available in the load path (i.e. loadable via `require`) within your hook\n# runs. Note that having a Gemfile requires you to include `overcommit` itself\n# in your Gemfile (otherwise Overcommit can't load itself!).\n#\n# This is useful if you want to:\n#\n#   - Enforce a specific version of Overcommit to use for all hook runs\n#     (or to use a version from the master branch that has not been released yet)\n#   - Enforce a specific version or unreleased branch is used for a gem you want\n#     to use in your git hooks\n#\n# WARNING: This makes your hook runs slower, but you can work around this!\n#\n# Loading a Bundler context necessarily adds a startup delay to your hook runs\n# as Bundler parses the Gemfile and checks that the dependencies are satisfied.\n# Thus for projects with many gems this can introduce a noticeable delay.\n#\n# The recommended workaround is to create a separate Gemfile in the root of your\n# repository (call it `.overcommit_gems.rb`), and include only the gems that\n# your Overcommit hooks need in order to run. This significantly reduces the\n# startup delay in your hook runs. Make sure to commit both\n# `.overcommit_gems.rb` and the resulting `.overcommit_gems.rb.lock` file to\n# your repository, and then set the `gemfile` option below to the name you gave\n# the file.\n# (Generate lock file by running `bundle install --gemfile=.overcommit_gems.rb`)\n#\n# NOTE: the following line will be parsed by a regexp rather than a proper YAML\n# parser, so avoid any values other than false or a string, and don't use inline\n# comments\ngemfile: false\n\n# Where to store hook plugins specific to a repository. These are loaded in\n# addition to the default hooks Overcommit comes with. The location is relative\n# to the root of the repository.\nplugin_directory: '.git-hooks'\n\n# Whether to hide hook output by default. This results in completely silent hook\n# runs except in the case of warning or failure.\nquiet: false\n\n# Number of hooks that can be run concurrently. Typically this won't need to be\n# adjusted, but if you know that some of your hooks themselves use multiple\n# processors you can lower this value accordingly. You can define\n# single-operator mathematical expressions, e.g. '%{processors} * 2', or\n# '%{processors} / 2'.\nconcurrency: '%{processors}'\n\n# Whether to check if a hook plugin has changed since Overcommit last ran it.\n# This is a defense mechanism when working with repositories which can contain\n# untrusted code (e.g. when you fetch a pull request from a third party).\n# See https://github.com/brigade/overcommit#security for more information.\nverify_signatures: true\n\n# Hooks that are run against every commit message after a user has written it.\n# These hooks are useful for enforcing policies on commit messages written for a\n# project.\nCommitMsg:\n  ALL:\n    requires_files: false\n    quiet: false\n\n  CapitalizedSubject:\n    enabled: true\n    description: 'Check subject capitalization'\n\n  EmptyMessage:\n    enabled: true\n    description: 'Check for empty commit message'\n    quiet: true\n\n  GerritChangeId:\n    enabled: false\n    description: 'Ensure Gerrit Change-Id is present'\n    required: true\n\n  HardTabs:\n    enabled: false\n    description: 'Check for hard tabs'\n\n  MessageFormat:\n    enabled: false\n    description: 'Check commit message matches expected pattern'\n    pattern: '(.+)[|](.+)[|](.+)'\n    expected_pattern_message: '<Issue Id> | <Commit Message Description> | <Developer(s)>'\n    sample_message: 'DEFECT-1234 | Refactored Onboarding flow | John Doe'\n\n  RussianNovel:\n    enabled: false\n    description: 'Check length of commit message'\n    quiet: true\n\n  SingleLineSubject:\n    enabled: true\n    description: 'Check subject line'\n\n  SpellCheck:\n    enabled: false\n    description: 'Check for misspelled words'\n    required_executable: 'hunspell'\n    flags: ['-a']\n\n  TextWidth:\n    enabled: true\n    description: 'Check text width'\n    max_subject_width: 60\n    min_subject_width: 0\n    max_body_width: 72\n\n  TrailingPeriod:\n    enabled: true\n    description: 'Check for trailing periods in subject'\n\n# Hooks that are run after `git commit` is executed, before the commit message\n# editor is displayed. These hooks are ideal for syntax checkers, linters, and\n# other checks that you want to run before you allow a commit object to be\n# created.\nPreCommit:\n  ALL:\n    problem_on_unmodified_line: report\n    requires_files: true\n    required: false\n    quiet: false\n\n  AuthorEmail:\n    enabled: true\n    description: 'Check author email'\n    requires_files: false\n    required: true\n    quiet: true\n    pattern: '^[^@]+@.*$'\n\n  AuthorName:\n    enabled: true\n    description: 'Check for author name'\n    requires_files: false\n    required: true\n    quiet: true\n\n  BerksfileCheck:\n    enabled: false\n    description: 'Check Berksfile lock'\n    required_executable: 'berks'\n    flags: ['list', '--quiet']\n    install_command: 'gem install berks'\n    include:\n      - 'Berksfile'\n      - 'Berksfile.lock'\n\n  BrokenSymlinks:\n    enabled: true\n    description: 'Check for broken symlinks'\n    quiet: true\n\n  BundleAudit:\n    enabled: false\n    description: 'Check for vulnerable versions of gems'\n    required_executable: 'bundle-audit'\n    install_command: 'gem install bundler-audit'\n\n  BundleCheck:\n    enabled: false\n    description: 'Check Gemfile dependencies'\n    required_executable: 'bundle'\n    flags: ['check']\n    install_command: 'gem install bundler'\n    include:\n      - 'Gemfile'\n      - 'Gemfile.lock'\n      - '*.gemspec'\n\n  BundleOutdated:\n    enabled: false\n    description: 'List installed gems with newer versions available'\n    required_executable: 'bundle'\n    flags: ['outdated', '--strict', '--parseable']\n    install_command: 'gem install bundler'\n\n  CaseConflicts:\n    enabled: true\n    description: 'Check for case-insensitivity conflicts'\n    quiet: true\n\n  ChamberCompare:\n    enabled: false\n    description: 'Check that settings are equivalent between namespaces'\n    required_executable: 'chamber'\n    flags: ['compare']\n    install_command: 'gem install chamber'\n    namespaces:\n      - ['development']\n      - ['test']\n      - ['production']\n    exclusions: []\n    include: &chamber_settings_files\n      - 'config/settings*.yml'\n      - 'config/settings*.yml.erb'\n      - 'config/settings/**/*.yml'\n      - 'config/settings/**/*.yml.erb'\n      - 'settings*.yml'\n      - 'settings*.yml.erb'\n      - 'settings/**/*.yml'\n      - 'settings/**/*.yml.erb'\n\n  ChamberSecurity:\n    enabled: false\n    description: 'Check that settings have been secured with Chamber'\n    required_executable: 'chamber'\n    flags: ['secure', '--files']\n    install_command: 'gem install chamber'\n    include: *chamber_settings_files\n\n  ChamberVerification:\n    enabled: false\n    description: 'Verify that all settings changes have been approved'\n    required_executable: 'chamber'\n    flags: ['sign', '--verify']\n    install_command: 'gem install chamber'\n    include: *chamber_settings_files\n\n  CodeSpellCheck:\n    enabled: false\n    description: 'Check if all your code is spell-checked correctly'\n    command: 'alfonsox'\n    install_command: 'gem install alfonsox'\n    include:\n      - '**/*.rb'\n      - '**/*.erb'\n\n  CoffeeLint:\n    enabled: false\n    description: 'Analyze with coffeelint'\n    required_executable: 'coffeelint'\n    flags: ['--reporter=csv']\n    install_command: 'npm install -g coffeelint'\n    include: '**/*.coffee'\n\n  CookStyle:\n    enabled: false\n    description: 'Analyze with CookStyle'\n    required_executable: 'cookstyle'\n    flags: ['--format=emacs', '--force-exclusion', '--display-cop-names']\n    install_command: 'gem install cookstyle'\n    include:\n      - '**/*.rb'\n      - '**/*.erb'\n\n  Credo:\n    enabled: false\n    description: 'Analyze with credo'\n    required_executable: 'mix'\n    flags: ['credo', '--all', '--strict', '--format', 'flycheck']\n    include:\n      - '**/*.ex'\n      - '**/*.exs'\n\n  CssLint:\n    enabled: false\n    description: 'Analyze with csslint'\n    required_executable: 'csslint'\n    flags: ['--quiet', '--format=compact']\n    install_command: 'npm install -g csslint'\n    include: '**/*.css'\n\n  DartAnalyzer:\n    enabled: false\n    description: 'Analyze with dartanalyzer'\n    required_executable: 'dartanalyzer'\n    flags: []\n    include:\n      - '**/*.dart'\n\n  Dogma:\n    enabled: false\n    description: 'Analyze with dogma'\n    required_executable: 'mix'\n    flags: ['dogma']\n    include:\n      - '**/*.ex'\n      - '**/*.exs'\n\n  ErbLint:\n    enabled: false\n    description: 'Analyze with ERB Lint'\n    required_executable: 'erblint'\n    install_command: 'bundle install erb_lint'\n    include: '**/*.html.erb'\n\n  EsLint:\n    enabled: false\n    description: 'Analyze with ESLint'\n    required_executable: 'eslint'\n    flags: ['--format=compact']\n    install_command: 'npm install -g eslint'\n    include: '**/*.js'\n\n  ExecutePermissions:\n    enabled: false\n    description: 'Check for file execute permissions'\n    quiet: true\n\n  Fasterer:\n    enabled: false\n    description: 'Analyzing for potential speed improvements'\n    required_executable: 'fasterer'\n    install_command: 'gem install fasterer'\n    include: '**/*.rb'\n\n  FixMe:\n    enabled: false\n    description: 'Check for \"token\" strings'\n    required_executable: 'grep'\n    flags: ['-IEHnw']\n    keywords: ['BROKEN', 'BUG', 'ERROR', 'FIXME', 'HACK', 'NOTE', 'OPTIMIZE', 'REVIEW', 'TODO', 'WTF', 'XXX']\n\n  FileSize:\n    enabled: false\n    description: 'Check for oversized files'\n    size_limit_bytes: 1_000_000\n\n  Flay:\n    enabled: false\n    description: 'Analyze ruby code for structural similarities with Flay'\n    required_executable: 'flay'\n    install_command: 'gem install flay'\n    mass_threshold: 16\n    fuzzy: 1\n    liberal: false\n    include: '**/*.rb'\n\n  Foodcritic:\n    enabled: false\n    description: 'Analyze with Foodcritic'\n    required_executable: 'foodcritic'\n    flags: ['--epic-fail=any']\n    install_command: 'gem install foodcritic'\n\n  ForbiddenBranches:\n    enabled: false\n    description: 'Check for commit to forbidden branch'\n    quiet: true\n    branch_patterns: ['master']\n\n  GinkgoFocus:\n    enabled: false\n    description: 'Check for \"focused\" tests'\n    required_executable: 'grep'\n    flags: ['-IEHnw']\n    keywords: ['FContext','FDescribe','FIt','FMeasure','FSpecify','FWhen']\n\n  GoFmt:\n    enabled: false\n    description: 'Fix with go fmt'\n    required_executable: 'go'\n    command: ['go', 'fmt']\n    parallelize: false\n    include: '**/*.go'\n\n  GolangciLint:\n    enabled: false\n    description: 'Analyze with golangci-lint'\n    required_executable: 'golangci-lint'\n    install_command: 'go get github.com/golangci/golangci-lint/cmd/golangci-lint'\n    flags: ['--out-format=line-number', '--print-issued-lines=false']\n    command: ['golangci-lint', 'run']\n    include: '**/*.go'\n\n  GoLint:\n    enabled: false\n    description: 'Analyze with golint'\n    required_executable: 'golint'\n    install_command: 'go get github.com/golang/lint/golint'\n    include: '**/*.go'\n\n  GoVet:\n    enabled: false\n    description: 'Analyze with go vet'\n    required_executable: 'go'\n    flags: ['tool', 'vet']\n    install_command: 'go get golang.org/x/tools/cmd/vet'\n    include: '**/*.go'\n\n  Hadolint:\n    enabled: false\n    description: 'Analyze with hadolint'\n    required_executable: 'hadolint'\n    include:\n      - '**/Dockerfile*'\n\n  HamlLint:\n    enabled: false\n    description: 'Analyze with haml-lint'\n    required_executable: 'haml-lint'\n    install_command: 'gem install haml-lint'\n    flags: ['--no-summary']\n    include: '**/*.haml'\n\n  HardTabs:\n    enabled: false\n    description: 'Check for hard tabs'\n    quiet: true\n    required_executable: 'grep'\n    flags: ['-IHn', \"\\t\"]\n    exclude:\n      - '**/Makefile'\n      - '**/*.go'\n\n  Hlint:\n    enabled: false\n    description: 'Analyze with hlint'\n    required_executable: 'hlint'\n    install_command: 'cabal install hlint'\n    include: '**/*.hs'\n\n  HtmlHint:\n    enabled: false\n    description: 'Analyze with HTMLHint'\n    required_executable: 'htmlhint'\n    install_command: 'npm install -g htmlhint'\n    include: '**/*.html'\n\n  HtmlTidy:\n    enabled: false\n    description: 'Analyze HTML with tidy'\n    required_executable: 'tidy'\n    flags: ['-errors', '-quiet', '-utf8']\n    include: '**/*.html'\n\n  ImageOptim:\n    enabled: false\n    description: 'Check for optimizable images'\n    required_executable: 'image_optim'\n    install_command: 'gem install image_optim'\n    include:\n      - '**/*.gif'\n      - '**/*.jpeg'\n      - '**/*.jpg'\n      - '**/*.png'\n      - '**/*.svg'\n\n  JavaCheckstyle:\n    enabled: false\n    description: 'Analyze with checkstyle'\n    required_executable: 'checkstyle'\n    flags: ['-c', '/sun_checks.xml']\n    include: '**/*.java'\n\n  Jscs:\n    enabled: false\n    description: 'Analyze with JSCS'\n    required_executable: 'jscs'\n    flags: ['--reporter=inline']\n    install_command: 'npm install -g jscs'\n    include: '**/*.js'\n\n  JsHint:\n    enabled: false\n    description: 'Analyze with JSHint'\n    required_executable: 'jshint'\n    flags: ['--verbose']\n    install_command: 'npm install -g jshint'\n    include: '**/*.js'\n\n  JsLint:\n    enabled: false\n    description: 'Analyze with JSLint'\n    required_executable: 'jslint'\n    flags: ['--terse']\n    install_command: 'npm install -g jslint'\n    include: '**/*.js'\n\n  Jsl:\n    enabled: false\n    description: 'Analyze with JSL'\n    required_executable: 'jsl'\n    flags: ['-nologo', '-nofilelisting', '-nocontext', '-nosummary']\n    include: '**/*.js'\n\n  JsonSyntax:\n    enabled: false\n    description: 'Validate JSON syntax'\n    required_library: 'json'\n    install_command: 'gem install json'\n    include: '**/*.json'\n\n  KtLint:\n    enabled: false\n    description: 'Analyze with KtLint'\n    required_executable: 'ktlint'\n    flags: []\n    include: '**/*.kt'\n\n  LicenseFinder:\n    enabled: false\n    description: 'Analyze with LicenseFinder'\n    required_executable: 'license_finder'\n    install_command: 'gem install license_finder'\n    include:\n      - 'Gemfile'\n      - 'requirements.txt'\n      - 'package.json'\n      - 'pom.xml'\n      - 'build.gradle'\n      - 'bower.json'\n      - 'Podfile'\n      - 'rebar.config'\n\n  LicenseHeader:\n    enabled: false\n    license_file: 'LICENSE.txt'\n    description: 'Check source files for license headers'\n\n  LocalPathsInGemfile:\n    enabled: false\n    description: 'Check for local paths in Gemfile'\n    required_executable: 'grep'\n    flags: ['-IHnE', \"^[^#]*((\\\\bpath:)|(:path[ \\t]*=>))\"]\n    include: '**/Gemfile'\n\n  Mdl:\n    enabled: false\n    description: 'Analyze markdown files with mdl'\n    required_executable: 'mdl'\n    flags: ['--json']\n    install_command: 'gem install mdl'\n    include: '**/*.md'\n\n  MergeConflicts:\n    enabled: true\n    description: 'Check for merge conflicts'\n    quiet: true\n    required_executable: 'grep'\n    flags: ['-IHn', \"^<<<<<<<[ \\t]\"]\n\n  MixFormat:\n    enabled: false\n    description: 'Check formatting with mix format'\n    required_executable: 'mix'\n    flags: ['format', '--check-formatted']\n    include:\n      - '**/*.ex'\n      - '**/*.heex'\n      - '**/*.exs'\n\n  PuppetMetadataJsonLint:\n    enabled: false\n    description: 'Checking module metadata'\n    flags: ['--strict-license', '--strict-dependencies', '--fail-on-warning']\n    include: 'metadata.json'\n    required_executable: 'metadata-json-lint'\n    install_command: 'gem install metadata-json-lint'\n\n  NginxTest:\n    enabled: false\n    description: 'Test nginx configs'\n    required_executable: 'nginx'\n    flags: ['-t']\n    include: '**/nginx.conf'\n\n  Pep257:  # Deprecated – use Pydocstyle instead.\n    enabled: false\n    description: 'Analyze docstrings with pep257'\n    required_executable: 'pep257'\n    install_command: 'pip install pep257'\n    include: '**/*.py'\n\n  Pep8:  # Deprecated – use Pycodestyle instead.\n    enabled: false\n    description: 'Analyze with pep8'\n    required_executable: 'pep8'\n    install_command: 'pip install pep8'\n    include: '**/*.py'\n\n  PhpLint:\n    enabled: false\n    description: 'Testing with PHP lint'\n    required_executable: 'php'\n    command: 'php'\n    flags: ['-l']\n    include: '**/*.php'\n\n  PhpCs:\n    enabled: false\n    description: 'Analyze with PHP_CodeSniffer'\n    command: 'vendor/bin/phpcs'\n    flags: ['--standard=PSR2', '--report=csv']\n    include: '**/*.php'\n\n  PhpCsFixer:\n    enabled: false\n    description: 'Fix non compliant PHP files'\n    required_executable: 'php-cs-fixer'\n    command: 'vendor/bin/php-cs-fixer'\n    flags: ['fix', '-v', '--path-mode=intersection']\n    install_command: 'composer global require friendsofphp/php-cs-fixer'\n    include: '**/*.php'\n\n  PhpStan:\n    description: 'Analyze with phpstan'\n    enabled: false\n    command: 'phpstan'\n    flags: ['analyze', '--errorFormat=raw']\n    include:\n      - '**/*.php'\n\n  Pronto:\n    enabled: false\n    description: 'Analyzing with pronto'\n    required_executable: 'pronto'\n    install_command: 'gem install pronto'\n    flags: ['run', '--staged', '--exit-code']\n\n  PuppetLint:\n    enabled: false\n    description: 'Analyze with puppet-lint'\n    required_executable: 'puppet-lint'\n    install_command: 'gem install puppet-lint'\n    flags:\n      - '--log-format=\"%{fullpath}:%{line}:%{column}:%{KIND}: %{message} (%{check})\"'\n      - '--fail-on-warnings'\n      - '--error-level=all'\n    include: '**/*.pp'\n\n  Pycodestyle:\n    enabled: false\n    description: 'Analyze with pycodestyle'\n    required_executable: 'pycodestyle'\n    install_command: 'pip install pycodestyle'\n    include: '**/*.py'\n\n  Pydocstyle:\n    enabled: false\n    description: 'Analyze docstrings with pydocstyle'\n    required_executable: 'pydocstyle'\n    install_command: 'pip install pydocstyle'\n    include: '**/*.py'\n\n  Pyflakes:\n    enabled: false\n    description: 'Analyze with pyflakes'\n    required_executable: 'pyflakes'\n    install_command: 'pip install pyflakes'\n    include: '**/*.py'\n\n  Pylint:\n    enabled: false\n    description: 'Analyze with Pylint'\n    required_executable: 'pylint'\n    install_command: 'pip install pylint'\n    flags:\n      - '--msg-template=\"{path}:{line}:{C}: {msg} ({symbol})\"'\n      - '--reports=n'\n      - '--persistent=n'\n    include: '**/*.py'\n\n  PythonFlake8:\n    enabled: false\n    description: 'Analyze with flake8'\n    required_executable: 'flake8'\n    install_command: 'pip install flake8'\n    include: '**/*.py'\n\n  RakeTarget:\n    enabled: false\n    description: 'Run rake targets'\n    # targets:\n    #  - 'lint'\n    #  - 'validate'\n    #  - '...'\n    required_executable: 'rake'\n    install_command: 'gem install rake'\n\n  RailsBestPractices:\n    enabled: false\n    description: 'Analyze with RailsBestPractices'\n    required_executable: 'rails_best_practices'\n    flags: ['--without-color']\n    install_command: 'gem install rails_best_practices'\n\n  RailsSchemaUpToDate:\n    enabled: false\n    description: 'Check if database schema is up to date'\n    include:\n      - 'db/migrate/*.rb'\n      - 'db/schema.rb'\n      - 'db/structure.sql'\n\n  Reek:\n    enabled: false\n    description: 'Analyze with Reek'\n    required_executable: 'reek'\n    flags: ['--single-line', '--no-color', '--force-exclusion']\n    install_command: 'gem install reek'\n    include:\n      - '**/*.gemspec'\n      - '**/*.rake'\n      - '**/*.rb'\n      - '**/Gemfile'\n      - '**/Rakefile'\n\n  RstLint:\n    enabled: false\n    description: 'Analyze reStructuredText files with rst-lint'\n    required_executable: 'rst-lint'\n    install_command: 'pip install restructuredtext_lint'\n    include: '**/*.rst'\n\n  RSpec:\n    enabled: false\n    description: 'Run tests with Rspec'\n    required_executable: 'rspec'\n\n  RuboCop:\n    enabled: false\n    description: 'Analyze with RuboCop'\n    required_executable: 'rubocop'\n    flags: ['--format=emacs', '--force-exclusion', '--display-cop-names']\n    install_command: 'gem install rubocop'\n    include:\n      - '**/*.gemspec'\n      - '**/*.rake'\n      - '**/*.rb'\n      - '**/*.ru'\n      - '**/Gemfile'\n      - '**/Rakefile'\n\n  RubyLint:\n    enabled: false\n    description: 'Analyze with ruby-lint'\n    required_executable: 'ruby-lint'\n    flags: ['--presenter=syntastic', '--levels=error,warning']\n    install_command: 'gem install ruby-lint'\n    include:\n      - '**/*.gemspec'\n      - '**/*.rb'\n\n  RubySyntax:\n    enabled: false\n    description: 'Check ruby syntax'\n    required_executable: 'ruby'\n    command: [\n      'ruby',\n      '-e',\n      'ARGV.each { |applicable_file| ruby_c_output = `ruby -c #{applicable_file}`; puts ruby_c_output unless $?.success? }'\n    ]\n    include:\n      - '**/*.gemspec'\n      - '**/*.rb'\n\n  Scalariform:\n    enabled: false\n    description: 'Check formatting with Scalariform'\n    required_executable: 'scalariform'\n    flags: ['--test']\n    include: '**/*.scala'\n\n  Scalastyle:\n    enabled: false\n    description: 'Analyze with Scalastyle'\n    required_executable: 'scalastyle'\n    include: '**/*.scala'\n\n  ScssLint:\n    enabled: false\n    description: 'Analyze with scss-lint'\n    required_library: 'json'\n    required_executable: 'scss-lint'\n    flags: ['--format', 'JSON']\n    install_command: 'gem install scss_lint'\n    include: '**/*.scss'\n\n  SemiStandard:\n    enabled: false\n    description: 'Analyze with semistandard'\n    required_executable: 'semistandard'\n    flags: ['--verbose']\n    install_command: 'npm install -g semistandard'\n    include: '**/*.js'\n\n  ShellCheck:\n    enabled: false\n    description: 'Analyze with ShellCheck'\n    required_executable: 'shellcheck'\n    flags: ['--format=gcc']\n    include: '**/*.sh'\n\n  SlimLint:\n    enabled: false\n    description: 'Analyze with slim-lint'\n    required_executable: 'slim-lint'\n    install_command: 'gem install slim_lint'\n    include: '**/*.slim'\n\n  Solargraph:\n    enabled: false\n    description: 'Typecheck with Solargraph'\n    requires_files: true\n    required_executable: 'solargraph'\n    install_command: 'gem install solargraph'\n    flags: ['typecheck', '--level', 'strong']\n    include: '**/*.rb'\n    exclude:\n      - 'spec/**/*.rb'\n      - 'test/**/*.rb'\n      - 'vendor/**/*.rb'\n      - '.bundle/**/*.rb'\n\n  Sorbet:\n    enabled: false\n    description: 'Analyze with Sorbet'\n    required_executable: 'srb'\n    install_command: 'gem install sorbet'\n    command: ['srb', 'tc']\n    include: '**/*.rb'\n\n  Sqlint:\n    enabled: false\n    description: 'Analyze with sqlint'\n    required_executable: 'sqlint'\n    install_command: 'gem install sqlint'\n    include: '**/*.sql'\n\n  Standard:\n    enabled: false\n    description: 'Analyze with standard'\n    required_executable: 'standard'\n    flags: ['--verbose']\n    install_command: 'npm install -g standard'\n    include: '**/*.js'\n\n  Stylelint:\n    enabled: false\n    description: 'Check styles with Stylelint'\n    required_executable: 'stylelint'\n    flags: ['-f', 'compact']\n    install_command: 'npm install -g stylelint'\n    include:\n      - '**/*.scss'\n      - '**/*.css'\n      - '**/*.less'\n\n  SwiftLint:\n    enabled: false\n    description: 'Analyze with SwiftLint'\n    required_executable: 'swiftlint'\n    flags: ['lint', '--strict']\n    install_command: 'brew install swiftlint'\n    include: '**/*.swift'\n\n  TerraformFormat:\n    enabled: false\n    description: 'Analyze with Terraform'\n    required_executable: 'terraform'\n    flags: ['fmt', '-check=true', '-diff=false']\n    include: '**/*.tf'\n\n  TsLint:\n    enabled: false\n    description: 'Analyze with TSLint'\n    required_executable: 'tslint'\n    install_command: 'npm install -g tslint typescript'\n    include: '**/*.ts'\n\n  TrailingWhitespace:\n    enabled: false\n    description: 'Check for trailing whitespace'\n    required_executable: 'grep'\n    flags: ['-IHn', \"[ \\t]$\"]\n\n  TravisLint:\n    enabled: false\n    description: 'Check Travis CI configuration'\n    required_executable: 'travis'\n    flags: ['lint']\n    install_command: 'gem install travis'\n    include: '.travis.yml'\n\n  Vint:\n    enabled: false\n    description: 'Analyze with Vint'\n    required_executable: 'vint'\n    install_command: 'pip install vim-vint'\n    include:\n      - '**/*.vim'\n      - '**/*.vimrc'\n\n  W3cCss:\n    enabled: false\n    description: 'Analyze with W3C CSS validation service'\n    required_library: 'w3c_validators'\n    install_command: 'gem install w3c_validators'\n    validator_uri: 'http://jigsaw.w3.org/css-validator/validator'\n    language: 'en'\n    profile: 'css3'\n    warn_level: 2\n    include:\n      - '**/*.css'\n\n  W3cHtml:\n    enabled: false\n    description: 'Analyze with W3C HTML validation service'\n    required_library: 'w3c_validators'\n    install_command: 'gem install w3c_validators'\n    validator_uri: 'https://validator.w3.org/nu'\n    charset: 'utf-8'\n    doctype: 'HTML5'\n    include:\n      - '**/*.html'\n\n  LineEndings:\n    description: 'Check line endings'\n    enabled: false\n    eol: \"\\n\" # or \"\\r\\n\" for Windows-style newlines\n\n  XmlLint:\n    enabled: false\n    description: 'Analyze with xmllint'\n    required_executable: 'xmllint'\n    flags: ['--noout']\n    include:\n      - '**/*.xml'\n      - '**/*.svg'\n\n  XmlSyntax:\n    enabled: false\n    description: 'Check XML syntax'\n    required_library: 'rexml/document'\n    include:\n      - '**/*.xml'\n      - '**/*.svg'\n\n  YamlLint:\n    enabled: false\n    description: 'Analyze with YAMLlint'\n    required_executable: 'yamllint'\n    flags: ['--format=parsable', '--strict']\n    install_command: 'pip install yamllint'\n    include:\n      - '**/*.yaml'\n      - '**/*.yml'\n\n  YamlSyntax:\n    enabled: false\n    description: 'Check YAML syntax'\n    required_library: 'yaml'\n    include:\n      - '**/*.yaml'\n      - '**/*.yml'\n\n  YardCoverage:\n    enabled: false\n    description: 'Checking for yard coverage'\n    command: ['yard', 'stats', '--list-undoc', '--compact']\n    flags: ['--private', '--protected']\n    required_executable: 'yard'\n    install_command: 'gem install yard'\n    min_coverage_percentage: 100\n    include:\n      - '/**/*.rb'\n\n  YarnCheck:\n    enabled: false\n    description: 'Check yarn.lock dependencies'\n    required_executable: 'yarn'\n    flags: ['check', '--silent', '--no-progress', '--non-interactive']\n    install_command: 'npm install --global yarn'\n    include:\n      - 'package.json'\n      - 'yarn.lock'\n\n# Hooks that run after HEAD changes or a file is explicitly checked out.\nPostCheckout:\n  ALL:\n    required: false\n    quiet: false\n    skip_file_checkout: true\n\n  BowerInstall:\n    enabled: false\n    description: 'Install bower dependencies'\n    requires_files: true\n    required_executable: 'bower'\n    install_command: 'npm install -g bower'\n    flags: ['install']\n    include: 'bower.json'\n\n  BundleInstall:\n    enabled: false\n    description: 'Install Bundler dependencies'\n    requires_files: true\n    required_executable: 'bundle'\n    install_command: 'gem install bundler'\n    flags: ['install']\n    include:\n      - 'Gemfile'\n      - 'Gemfile.lock'\n      - '*.gemspec'\n\n  ComposerInstall:\n    enabled: false\n    description: 'Install composer dependencies'\n    requires_files: true\n    required_executable: 'composer'\n    install_command: 'curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer'\n    flags: ['install']\n    include: 'composer.json'\n\n  GitLfs:\n    enabled: false\n    description: 'Check status of lockable files tracked by Git LFS'\n    required_executable: 'git-lfs'\n    install_command: 'brew install git-lfs'\n\n  IndexTags:\n    enabled: false\n    description: 'Generate tags file from source'\n    quiet: true\n    required_executable: 'ctags'\n\n  NpmInstall:\n    enabled: false\n    description: 'Install NPM dependencies'\n    requires_files: true\n    required_executable: 'npm'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'npm-shrinkwrap.json'\n\n  SubmoduleStatus:\n    enabled: false\n    description: 'Check submodule status'\n    quiet: true\n    recursive: false\n\n  YarnInstall:\n    enabled: false\n    description: 'Install Yarn dependencies'\n    requires_files: true\n    required_executable: 'yarn'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'yarn.lock'\n\n# Hooks that run after a commit is created.\nPostCommit:\n  ALL:\n    requires_files: false\n    required: false\n    quiet: false\n\n  BowerInstall:\n    enabled: false\n    description: 'Install bower dependencies'\n    requires_files: true\n    required_executable: 'bower'\n    install_command: 'npm install -g bower'\n    flags: ['install']\n    include: 'bower.json'\n\n  BundleInstall:\n    enabled: false\n    description: 'Install Bundler dependencies'\n    requires_files: true\n    required_executable: 'bundle'\n    install_command: 'gem install bundler'\n    flags: ['install']\n    include:\n      - 'Gemfile'\n      - 'Gemfile.lock'\n      - '*.gemspec'\n\n  Commitplease:\n    enabled: false\n    description: 'Analyze with Commitplease'\n    required_executable: './node_modules/.bin/commitplease'\n    install_command: 'npm install --save-dev commitplease'\n    flags: ['-1']\n\n  ComposerInstall:\n    enabled: false\n    description: 'Install composer dependencies'\n    requires_files: true\n    required_executable: 'composer'\n    install_command: 'curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer'\n    flags: ['install']\n    include: 'composer.json'\n\n  GitGuilt:\n    enabled: false\n    description: 'Calculate changes in blame since last commit'\n    requires_files: true\n    required_executable: 'git-guilt'\n    flags: ['HEAD~', 'HEAD']\n    install_command: 'npm install -g git-guilt'\n\n  GitLfs:\n    enabled: false\n    description: 'Check status of lockable files tracked by Git LFS'\n    required_executable: 'git-lfs'\n    install_command: 'brew install git-lfs'\n\n  IndexTags:\n    enabled: false\n    description: 'Generate tags file from source'\n    quiet: true\n    required_executable: 'ctags'\n\n  NpmInstall:\n    enabled: false\n    description: 'Install NPM dependencies'\n    requires_files: true\n    required_executable: 'npm'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'npm-shrinkwrap.json'\n\n  SubmoduleStatus:\n    enabled: false\n    description: 'Check submodule status'\n    quiet: true\n    recursive: false\n\n  YarnInstall:\n    enabled: false\n    description: 'Install Yarn dependencies'\n    requires_files: true\n    required_executable: 'yarn'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'yarn.lock'\n\n# Hooks that run after `git merge` executes successfully (no merge conflicts).\nPostMerge:\n  ALL:\n    requires_files: false\n    quiet: false\n\n  BowerInstall:\n    enabled: false\n    description: 'Install bower dependencies'\n    requires_files: true\n    required_executable: 'bower'\n    install_command: 'npm install -g bower'\n    flags: ['install']\n    include: 'bower.json'\n\n  BundleInstall:\n    enabled: false\n    description: 'Install Bundler dependencies'\n    requires_files: true\n    required_executable: 'bundle'\n    install_command: 'gem install bundler'\n    flags: ['install']\n    include:\n      - 'Gemfile'\n      - 'Gemfile.lock'\n      - '*.gemspec'\n\n  ComposerInstall:\n    enabled: false\n    description: 'Install composer dependencies'\n    requires_files: true\n    required_executable: 'composer'\n    install_command: 'curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer'\n    flags: ['install']\n    include: 'composer.json'\n\n  GitLfs:\n    enabled: false\n    description: 'Check status of lockable files tracked by Git LFS'\n    required_executable: 'git-lfs'\n    install_command: 'brew install git-lfs'\n\n  IndexTags:\n    enabled: false\n    description: 'Generate tags file from source'\n    quiet: true\n    required_executable: 'ctags'\n\n  NpmInstall:\n    enabled: false\n    description: 'Install NPM dependencies'\n    requires_files: true\n    required_executable: 'npm'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'npm-shrinkwrap.json'\n\n  SubmoduleStatus:\n    enabled: false\n    description: 'Check submodule status'\n    quiet: true\n    recursive: false\n\n  YarnInstall:\n    enabled: false\n    description: 'Install Yarn dependencies'\n    requires_files: true\n    required_executable: 'yarn'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'yarn.lock'\n\n# Hooks that run after a commit is modified by an amend or rebase.\nPostRewrite:\n  ALL:\n    requires_files: false\n    quiet: false\n\n  BowerInstall:\n    enabled: false\n    description: 'Install bower dependencies'\n    requires_files: true\n    required_executable: 'bower'\n    install_command: 'npm install -g bower'\n    flags: ['install']\n    include: 'bower.json'\n\n  BundleInstall:\n    enabled: false\n    description: 'Install Bundler dependencies'\n    requires_files: true\n    required_executable: 'bundle'\n    install_command: 'gem install bundler'\n    flags: ['install']\n    include:\n      - 'Gemfile'\n      - 'Gemfile.lock'\n      - '*.gemspec'\n\n  ComposerInstall:\n    enabled: false\n    description: 'Install composer dependencies'\n    requires_files: true\n    required_executable: 'composer'\n    install_command: 'curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer'\n    flags: ['install']\n    include: 'composer.json'\n\n  IndexTags:\n    enabled: false\n    description: 'Generate tags file from source'\n    quiet: true\n    required_executable: 'ctags'\n\n  NpmInstall:\n    enabled: false\n    description: 'Install NPM dependencies'\n    requires_files: true\n    required_executable: 'npm'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'npm-shrinkwrap.json'\n\n  SubmoduleStatus:\n    enabled: false\n    description: 'Check submodule status'\n    quiet: true\n    recursive: false\n\n  YarnInstall:\n    enabled: false\n    description: 'Install Yarn dependencies'\n    requires_files: true\n    required_executable: 'yarn'\n    flags: ['install']\n    include:\n      - 'package.json'\n      - 'yarn.lock'\n\n# Hooks that run during the `prepare-commit-msg` hook.\nPrepareCommitMsg:\n  ALL:\n    requires_files: false\n    required: false\n    quiet: false\n\n  ReplaceBranch:\n    enabled: false\n    description: 'Prepends the commit message with text based on the branch name'\n    branch_pattern: '\\A(\\d+)-(\\w+).*\\z'\n    replacement_text: '[#\\1]'\n    skipped_commit_types:\n     - 'message'  # if message is given via `-m`, `-F`\n     - 'template' # if `-t` is given or `commit.template` is set\n     - 'commit'   # if `-c`, `-C`, or `--amend` is given\n     - 'merge'    # if merging\n     - 'squash'   # if squashing\n    on_fail: warn\n\n# Hooks that run during `git push`, after remote refs have been updated but\n# before any objects have been transferred.\nPrePush:\n  ALL:\n    requires_files: false\n    required: false\n    quiet: false\n\n  Brakeman:\n    enabled: false\n    description: 'Check for security vulnerabilities'\n    required_executable: 'brakeman'\n    flags: ['--exit-on-warn', '--quiet', '--summary']\n    install_command: 'gem install brakeman'\n\n  CargoTest:\n    enabled: false\n    description: 'Run tests with cargo'\n    required_executable: 'cargo'\n    flags: ['test']\n    include: 'src/**/*.rs'\n\n  FlutterTest:\n    enabled: false\n    description: 'Run flutter test suite'\n    required_executable: 'flutter'\n    flags: ['test']\n\n  GitLfs:\n    enabled: false\n    description: 'Upload files tracked by Git LFS'\n    required_executable: 'git-lfs'\n    install_command: 'brew install git-lfs'\n\n  GolangciLint:\n    enabled: false\n    description: 'Analyze with golangci-lint'\n    required_executable: 'golangci-lint'\n    install_command: 'go get github.com/golangci/golangci-lint/cmd/golangci-lint'\n    flags: ['--out-format=line-number', '--print-issued-lines=false']\n    command: ['golangci-lint', 'run']\n\n  GoTest:\n    enabled: false\n    description: 'Run go test suite'\n    required_executable: 'go'\n    command: ['go', 'test', './...']\n\n  Minitest:\n    enabled: false\n    description: 'Run Minitest test suite'\n    command: ['ruby', '-Ilib:test', '-rminitest', \"-e 'exit! Minitest.run'\"]\n    include: 'test/**/*_test.rb'\n\n  MixTest:\n    enabled: false\n    description: 'Run mix test suite'\n    required_executable: 'mix'\n    flags: ['test']\n\n  PhpUnit:\n    enabled: false\n    description: 'Run PhpUnit test suite'\n    command: 'vendor/bin/phpunit'\n    flags: ['--bootstrap', 'vendor/autoload.php', 'tests']\n    install_command: 'composer require --dev phpunit/phpunit'\n\n  Pronto:\n    enabled: false\n    description: 'Analyzing with pronto'\n    required_executable: 'pronto'\n    install_command: 'gem install pronto'\n    flags: ['run', '--exit-code']\n\n  ProtectedBranches:\n    enabled: false\n    description: 'Check for illegal pushes to protected branches'\n    destructive_only: true\n    branches: ['master']\n\n  PubTest:\n    enabled: false\n    description: 'Run pub test suite'\n    required_executable: 'pub'\n    flags: ['run', 'test']\n\n  Pytest:\n    enabled: false\n    description: 'Run pytest test suite'\n    required_executable: 'pytest'\n    install_command: 'pip install -U pytest'\n\n  PythonNose:\n    enabled: false\n    description: 'Run nose test suite'\n    required_executable: 'nosetests'\n    install_command: 'pip install -U nose'\n\n  RSpec:\n    enabled: false\n    description: 'Run RSpec test suite'\n    required_executable: 'rspec'\n\n  RakeTarget:\n    enabled: false\n    description: 'Run rake targets'\n    # targets:\n    #  - 'lint'\n    #  - 'validate'\n    #  - '...'\n    required_executable: 'rake'\n    install_command: 'gem install rake'\n\n  TestUnit:\n    enabled: false\n    description: 'Run Test::Unit test suite'\n    command: ['ruby', '-Ilib:test', '-rtest/unit', \"-e 'exit! Test::Unit::AutoRunner.run'\"]\n\n# Hooks that run during `git rebase`, before any commits are rebased.\n# If a hook fails, the rebase is aborted.\nPreRebase:\n  ALL:\n    requires_files: false\n    required: false\n    quiet: false\n\n  MergedCommits:\n    enabled: false\n    description: 'Check for commits that have already been merged'\n    branches: ['master']\n"
  },
  {
    "path": "config/starter.yml",
    "content": "# Use this file to configure the Overcommit hooks you wish to use. This will\n# extend the default configuration defined in:\n# https://github.com/sds/overcommit/blob/master/config/default.yml\n#\n# At the topmost level of this YAML file is a key representing type of hook\n# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can\n# customize each hook, such as whether to only run it on certain files (via\n# `include`), whether to only display output if it fails (via `quiet`), etc.\n#\n# For a complete list of hooks, see:\n# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook\n#\n# For a complete list of options that you can use to customize hooks, see:\n# https://github.com/sds/overcommit#configuration\n#\n# Uncomment the following lines to make the configuration take effect.\n\n#PreCommit:\n#  RuboCop:\n#    enabled: true\n#    on_warn: fail # Treat all warnings as failures\n#\n#  TrailingWhitespace:\n#    enabled: true\n#    exclude:\n#      - '**/db/structure.sql' # Ignore trailing whitespace in generated files\n#\n#PostCheckout:\n#  ALL: # Special hook name that customizes all hooks of this type\n#    quiet: true # Change all post-checkout hooks to only display output on failure\n#\n#  IndexTags:\n#    enabled: true # Generate a tags file with `ctags` each time HEAD changes\n"
  },
  {
    "path": "lib/overcommit/cli.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit'\nrequire 'optparse'\n\nmodule Overcommit\n  # Responsible for parsing command-line options and executing appropriate\n  # application logic based on those options.\n  class CLI # rubocop:disable Metrics/ClassLength\n    def initialize(arguments, input, logger)\n      @arguments = arguments\n      @cli_options = {}\n      @input     = input\n      @log       = logger\n      @options   = {}\n\n      Overcommit::Utils.log = logger\n    end\n\n    def run\n      parse_arguments\n\n      case @options[:action]\n      when :install, :uninstall\n        install_or_uninstall\n      when :template_dir\n        print_template_directory_path\n      when :sign\n        sign\n      when :run_all\n        run_all\n      when :diff\n        diff\n      end\n    rescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n      puts e\n      exit 78 # EX_CONFIG\n    rescue Overcommit::Exceptions::HookContextLoadError => e\n      puts e\n      exit 64 # EX_USAGE\n    end\n\n    private\n\n    attr_reader :log\n\n    def parse_arguments\n      @parser = create_option_parser\n\n      begin\n        @parser.parse!(@arguments, into: @cli_options)\n\n        # Default action is to install\n        @options[:action] ||= :install\n\n        # Unconsumed arguments are our targets\n        @options[:targets] = @arguments\n      rescue OptionParser::InvalidOption => e\n        print_help @parser.help, e\n      end\n    end\n\n    def create_option_parser\n      OptionParser.new do |opts|\n        opts.banner = \"Usage: #{opts.program_name} [options] [target-repo]\"\n\n        add_information_options(opts)\n        add_installation_options(opts)\n        add_other_options(opts)\n      end\n    end\n\n    def add_information_options(opts)\n      opts.on_tail('-h', '--help', 'Show this message') do\n        print_help opts.help\n      end\n\n      opts.on_tail('-v', '--version', 'Show version') do\n        print_version(opts.program_name)\n      end\n\n      opts.on_tail('-l', '--list-hooks', 'List installed hooks') do\n        print_installed_hooks\n      end\n    end\n\n    def add_installation_options(opts)\n      opts.on('-u', '--uninstall', 'Remove Overcommit hooks from a repository') do\n        @options[:action] = :uninstall\n      end\n\n      opts.on('-i', '--install', 'Install Overcommit hooks in a repository') do\n        @options[:action] = :install\n      end\n\n      opts.on('-f', '--force', 'Overwrite any previously installed hooks') do\n        @options[:force] = true\n      end\n\n      opts.on('-r [hook]', '--run [hook]', 'Run specified hook against all git tracked files. Defaults to `pre_commit`.') do |arg| # rubocop:disable Layout/LineLength\n        @options[:action] = :run_all\n        @options[:hook_to_run] = arg ? arg.to_s : 'run-all'\n      end\n\n      opts.on('--diff [ref]', 'Run pre_commit hooks against the diff between a given ref. Defaults to `main`.') do |arg| # rubocop:disable Layout/LineLength\n        @options[:action] = :diff\n        arg\n      end\n    end\n\n    def add_other_options(opts)\n      opts.on('-s', '--sign [hook]', 'Update hook signatures', String) do |hook_to_sign|\n        @options[:hook_to_sign] = hook_to_sign if hook_to_sign.is_a?(String)\n        @options[:action] = :sign\n      end\n\n      opts.on('-t', '--template-dir', 'Print location of template directory') do\n        @options[:action] = :template_dir\n      end\n    end\n\n    def install_or_uninstall\n      if Array(@options[:targets]).empty?\n        @options[:targets] = [Overcommit::Utils.repo_root].compact\n      end\n\n      if @options[:targets].empty?\n        log.warning 'You are not in a git repository.'\n        log.log 'You must either specify the path to a repository or ' \\\n                'change your current directory to a repository.'\n        halt 64 # EX_USAGE\n      end\n\n      @options[:targets].each do |target|\n        Installer.new(log).run(target, @options)\n      rescue Overcommit::Exceptions::InvalidGitRepo => e\n        log.warning \"Invalid repo #{target}: #{e}\"\n        halt 69 # EX_UNAVAILABLE\n      rescue Overcommit::Exceptions::PreExistingHooks => e\n        log.warning \"Unable to install into #{target}: #{e}\"\n        halt 73 # EX_CANTCREAT\n      end\n    end\n\n    def print_template_directory_path\n      puts File.join(Overcommit::HOME, 'template-dir')\n      halt\n    end\n\n    def print_help(message, error = nil)\n      log.error \"#{error}\\n\" if error\n      log.log message\n      halt(error ? 64 : 0) # 64 = EX_USAGE\n    end\n\n    def print_version(program_name)\n      log.log \"#{program_name} #{Overcommit::VERSION}\"\n      halt\n    end\n\n    # Prints the hooks available in the current repo and whether they're\n    # enabled/disabled.\n    def print_installed_hooks\n      config.all_hook_configs.each do |hook_type, hook_configs|\n        print_hooks_for_hook_type(config, hook_configs, hook_type)\n      end\n\n      halt\n    end\n\n    def print_hooks_for_hook_type(repo_config, hook_configs, hook_type)\n      log.log \"#{hook_type}:\"\n      hook_configs.each do |hook_name, config|\n        log.partial \"  #{hook_name}: \"\n\n        if config['enabled']\n          log.success('enabled', true)\n        else\n          log.error('disabled', true)\n        end\n\n        if repo_config.plugin_hook?(hook_type, hook_name)\n          log.warning(' (plugin)')\n        else\n          log.newline\n        end\n      end\n    end\n\n    def sign\n      if @options[:hook_to_sign]\n        context = Overcommit::HookContext.create(@options[:hook_to_sign],\n                                                 config,\n                                                 @arguments,\n                                                 @input)\n        Overcommit::HookLoader::PluginHookLoader.new(config,\n                                                     context,\n                                                     log).update_signatures\n      else\n        log.log 'Updating signature for configuration file...'\n        config(verify: false).update_signature!\n      end\n\n      halt\n    end\n\n    def run_all\n      empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input\n      context = Overcommit::HookContext.create(@options[:hook_to_run], config, @arguments, empty_stdin) # rubocop:disable Layout/LineLength\n      config.apply_environment!(context, ENV)\n\n      printer = Overcommit::Printer.new(config, log, context)\n      runner  = Overcommit::HookRunner.new(config, log, context, printer)\n\n      status = runner.run\n\n      halt(status ? 0 : 65)\n    end\n\n    def diff\n      empty_stdin = File.open(File::NULL) # pre-commit hooks don't take input\n      context = Overcommit::HookContext.create('diff', config, @arguments, empty_stdin, **@cli_options) # rubocop:disable Layout/LineLength\n      config.apply_environment!(context, ENV)\n\n      printer = Overcommit::Printer.new(config, log, context)\n      runner  = Overcommit::HookRunner.new(config, log, context, printer)\n\n      status = runner.run\n\n      halt(status ? 0 : 65)\n    end\n\n    # Used for ease of stubbing in tests\n    def halt(status = 0)\n      exit status\n    end\n\n    # Returns the configuration for this repository.\n    def config(options = {})\n      @config ||= Overcommit::ConfigurationLoader.new(log, options).load_repo_config\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/command_splitter.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  # Distributes a list of arguments over multiple invocations of a command.\n  #\n  # This accomplishes the same functionality provided by `xargs` but in a\n  # cross-platform way that does not require any pre-existing tools.\n  #\n  # One of the tradeoffs with this approach is that we no longer deal with a\n  # single exit status from a command, but multiple (one for each invocation).\n  #\n  # This will return a struct similar to `Subprocess::Result` but with\n  # additional `statuses`, `stdouts`, and `stderrs` fields so hook authors can\n  # actually see the results of each invocation. If they don't care, the\n  # standard `status`, `stdout`, and `stderr` will still work but be a\n  # aggregation/concatenation of all statuses/outputs.\n  class CommandSplitter\n    # Encapsulates the result of a split argument run.\n    #\n    # @attr_reader statuses [Array<Integer>] status codes for invocations\n    # @attr_reader stdouts [Array<String>] standard outputs from invocations\n    # @attr_reader stderrs [Array<String>] standard error outputs from invocations\n    Result = Struct.new(:statuses, :stdouts, :stderrs) do\n      # Returns whether all invocations were successful.\n      #\n      # @return [true,false]\n      def success?\n        status == 0\n      end\n\n      # Returns `0` if all invocations returned `0`; `1` otherwise.\n      #\n      # @return [true,false]\n      def status\n        statuses.all? { |code| code == 0 } ? 0 : 1\n      end\n\n      # Returns concatenated standard output streams of all invocations in the\n      # order they were executed.\n      #\n      # @return [String]\n      def stdout\n        stdouts.join\n      end\n\n      # Returns concatenated standard error streams of all invocations in the\n      # order they were executed.\n      #\n      # @return [String]\n      def stderr\n        stderrs.join\n      end\n    end\n\n    class << self\n      def execute(initial_args, options)\n        options = options.dup\n\n        if (splittable_args = (options.delete(:args) { [] })).empty?\n          raise Overcommit::Exceptions::InvalidCommandArgs,\n                'Must specify list of arguments to split on'\n        end\n\n        # Execute each chunk of arguments in serial. We don't parallelize (yet)\n        # since in theory we want to support parallelization at the hook level\n        # and not within individual hooks.\n        results = extract_argument_lists(initial_args, splittable_args).map do |arg_list|\n          Overcommit::Subprocess.spawn(arg_list, options)\n        end\n\n        Result.new(results.map(&:status), results.map(&:stdout), results.map(&:stderr))\n      end\n\n      private\n\n      # Given a list of prefix arguments and suffix arguments that can be split,\n      # returns a list of argument lists that are executable on the current OS\n      # without exceeding command line limitations.\n      def extract_argument_lists(args, splittable_args)\n        # Total number of bytes needed to contain the prefix command\n        # (including byte separators between each argument)\n        prefix_bytes = (args.size - 1) + args.reduce(0) { |sum, arg| sum + arg.bytesize }\n\n        if prefix_bytes >= max_command_length\n          raise Overcommit::Exceptions::InvalidCommandArgs,\n                \"Command `#{args.take(5).join(' ')} ...` is longer than the \" \\\n                'maximum number of bytes allowed by the operating system ' \\\n                \"(#{max_command_length})\"\n        end\n\n        arg_lists = []\n        index = 0\n        while index <= splittable_args.length - 1\n          arg_list, index = arguments_under_limit(splittable_args,\n                                                  index,\n                                                  max_command_length - prefix_bytes)\n          arg_lists << args + arg_list\n        end\n\n        arg_lists\n      end\n\n      # @return [Array<Array<String>, Integer>] tuple of arguments and new index\n      def arguments_under_limit(splittable_args, start_index, byte_limit)\n        index = start_index\n        total_bytes = 0\n\n        loop do\n          break if index > splittable_args.length - 1\n\n          total_bytes += splittable_args[index].bytesize\n          break if total_bytes > byte_limit # Not enough room\n\n          index += 1\n        end\n\n        if index == start_index\n          # No argument was consumed; perhaps a really long argument?\n          raise Overcommit::Exceptions::InvalidCommandArgs,\n                \"Argument `#{splittable_args[index][0..5]}...` exceeds the \" \\\n                'maximum command length when appended to command prefix and ' \\\n                \"can't be split further\"\n        end\n\n        [splittable_args[start_index...index], index]\n      end\n\n      # Returns the maximum number of arguments allowed in a single command on\n      # this system.\n      #\n      # @return [Integer]\n      def max_command_length\n        @max_command_length ||=\n          if Gem.win_platform?\n            # Windows is limited to 2048 since that is a worst-case scenario.\n            # http://blogs.msdn.com/b/oldnewthing/archive/2003/12/10/56028.aspx\n            2048\n          else\n            # We fudge factor this by halving the buffer size since *nix systems\n            # usually have pretty large limits, and the actual limit changes\n            # depending on how much of your stack is environment variables.\n            # Definitely erring on the side of overly cautious.\n            `getconf ARG_MAX`.to_i / 2\n          end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/configuration.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest'\nrequire 'json'\n\nmodule Overcommit\n  # Stores configuration for Overcommit and the hooks it runs.\n  class Configuration # rubocop:disable Metrics/ClassLength\n    # Creates a configuration from the given hash.\n    #\n    # @param hash [Hash] loaded YAML config file as a hash\n    # @param options [Hash]\n    # @option default [Boolean] whether this is the default built-in configuration\n    # @option logger [Overcommit::Logger]\n    def initialize(hash, options = {})\n      @options = options.dup\n      @options[:logger] ||= Overcommit::Logger.silent\n      @hash = hash # Assign so validator can read original values\n      unless options[:validate] == false\n        @hash = Overcommit::ConfigurationValidator.new.validate(self, hash, options)\n      end\n    end\n\n    def ==(other)\n      super || @hash == other.hash\n    end\n\n    # Access the configuration as if it were a hash.\n    #\n    # @param key [String]\n    # @return [Array,Hash,Number,String]\n    def [](key)\n      @hash[key]\n    end\n\n    # Returns absolute path to the directory that external hook plugins should\n    # be loaded from.\n    def plugin_directory\n      File.join(Overcommit::Utils.repo_root, @hash['plugin_directory'] || '.git-hooks')\n    end\n\n    def concurrency\n      @concurrency ||=\n        begin\n          cores = Overcommit::Utils.processor_count\n          content = @hash.fetch('concurrency') { '%<processors>d' }\n          if content.is_a?(String)\n            concurrency_expr = content % { processors: cores }\n\n            a, op, b = concurrency_expr.scan(%r{(\\d+)\\s*([+\\-*\\/])\\s*(\\d+)})[0]\n            if a\n              a.to_i.send(op, b.to_i)\n            else\n              concurrency_expr.to_i\n            end\n          else\n            content.to_i\n          end\n        end\n    end\n\n    # Returns configuration for all hooks in each hook type.\n    #\n    # @return [Hash]\n    def all_hook_configs\n      smart_merge(all_builtin_hook_configs, all_plugin_hook_configs)\n    end\n\n    # Returns configuration for all built-in hooks in each hook type.\n    #\n    # @return [Hash]\n    def all_builtin_hook_configs\n      hook_configs = {}\n\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hook_names = @hash[hook_type].keys.reject { |name| name == 'ALL' }\n\n        hook_configs[hook_type] = Hash[\n          hook_names.map do |hook_name|\n            [hook_name, for_hook(hook_name, hook_type)]\n          end\n        ]\n      end\n\n      hook_configs\n    end\n\n    # Returns configuration for all plugin hooks in each hook type.\n    #\n    # @return [Hash]\n    def all_plugin_hook_configs\n      hook_configs = {}\n\n      Overcommit::Utils.supported_hook_types.each do |hook_type|\n        hook_type_class_name = Overcommit::Utils.camel_case(hook_type)\n\n        directory = File.join(plugin_directory, hook_type.tr('-', '_'))\n        plugin_paths = Dir[File.join(directory, '*.rb')].sort\n\n        hook_names = plugin_paths.map do |path|\n          Overcommit::Utils.camel_case(File.basename(path, '.rb'))\n        end\n\n        hook_configs[hook_type_class_name] = Hash[\n          hook_names.map do |hook_name|\n            [hook_name, for_hook(hook_name, Overcommit::Utils.camel_case(hook_type))]\n          end\n        ]\n      end\n\n      hook_configs\n    end\n\n    # Returns the built-in hooks that have been enabled for a hook type.\n    def enabled_builtin_hooks(hook_context)\n      @hash[hook_context.hook_class_name].keys.\n        reject { |hook_name| hook_name == 'ALL' }.\n        select { |hook_name| built_in_hook?(hook_context, hook_name) }.\n        select { |hook_name| hook_enabled?(hook_context, hook_name) }\n    end\n\n    # Returns the ad hoc hooks that have been enabled for a hook type.\n    def enabled_ad_hoc_hooks(hook_context)\n      @hash[hook_context.hook_class_name].keys.\n        reject { |hook_name| hook_name == 'ALL' }.\n        select { |hook_name| ad_hoc_hook?(hook_context, hook_name) }.\n        select { |hook_name| hook_enabled?(hook_context, hook_name) }\n    end\n\n    # Returns a non-modifiable configuration for a hook.\n    def for_hook(hook, hook_type = nil)\n      unless hook_type\n        components = hook.class.name.split('::')\n        hook = components.last\n        hook_type = components[-2]\n      end\n\n      # Merge hook configuration with special 'ALL' config\n      hook_config = smart_merge(@hash[hook_type]['ALL'], @hash[hook_type][hook] || {})\n\n      # Need to specially handle `enabled` option since not setting it does not\n      # necessarily mean the hook is disabled\n      hook_config['enabled'] = hook_enabled?(hook_type, hook)\n\n      hook_config.freeze\n    end\n\n    # Merges the given configuration with this one, returning a new\n    # {Configuration}. The provided configuration will either add to or replace\n    # any options defined in this configuration.\n    def merge(config)\n      self.class.new(smart_merge(@hash, config.hash))\n    end\n\n    # Applies additional configuration settings based on the provided\n    # environment variables.\n    def apply_environment!(hook_context, env)\n      skipped_hooks = \"#{env['SKIP']} #{env['SKIP_CHECKS']} #{env['SKIP_HOOKS']}\".split(/[:, ]/)\n      only_hooks = env.fetch('ONLY') { '' }.split(/[:, ]/)\n      hook_type = hook_context.hook_class_name\n\n      if only_hooks.any? || skipped_hooks.include?('all') || skipped_hooks.include?('ALL')\n        @hash[hook_type]['ALL']['skip'] = true\n      end\n\n      only_hooks.select { |hook_name| hook_exists?(hook_context, hook_name) }.\n                 map { |hook_name| Overcommit::Utils.camel_case(hook_name) }.\n                 each do |hook_name|\n        @hash[hook_type][hook_name] ||= {}\n        @hash[hook_type][hook_name]['skip'] = false\n      end\n\n      skipped_hooks.select { |hook_name| hook_exists?(hook_context, hook_name) }.\n                    map { |hook_name| Overcommit::Utils.camel_case(hook_name) }.\n                    each do |hook_name|\n        @hash[hook_type][hook_name] ||= {}\n        @hash[hook_type][hook_name]['skip'] = true\n      end\n    end\n\n    def plugin_hook?(hook_context_or_type, hook_name)\n      hook_type_name =\n        if hook_context_or_type.is_a?(String)\n          Overcommit::Utils.snake_case(hook_context_or_type)\n        else\n          hook_context_or_type.hook_type_name\n        end\n      hook_name = Overcommit::Utils.snake_case(hook_name)\n\n      File.exist?(File.join(plugin_directory, hook_type_name, \"#{hook_name}.rb\"))\n    end\n\n    # Return whether the signature for this configuration has changed since it\n    # was last calculated.\n    #\n    # @return [true,false]\n    def signature_changed?\n      signature != stored_signature\n    end\n\n    # Return whether a previous signature has been recorded for this\n    # configuration.\n    #\n    # @return [true,false]\n    def previous_signature?\n      !stored_signature.empty?\n    end\n\n    # Returns whether this configuration should verify itself by checking the\n    # stored configuration for the repo.\n    #\n    # @return [true,false]\n    def verify_signatures?\n      return false if ENV['OVERCOMMIT_NO_VERIFY']\n      return true if @hash['verify_signatures'] != false\n\n      result = Overcommit::Utils.execute(\n        %W[git config --local --get #{verify_signature_config_key}]\n      )\n\n      if result.status == 1 # Key doesn't exist\n        return true\n      elsif result.status != 0\n        raise Overcommit::Exceptions::GitConfigError,\n              \"Unable to read from local repo git config: #{result.stderr}\"\n      end\n\n      # We don't cast since we want to allow anything to count as \"true\" except\n      # a literal zero\n      result.stdout.strip != '0'\n    end\n\n    # Update the currently stored signature for this hook.\n    def update_signature!\n      result = Overcommit::Utils.execute(\n        %w[git config --local] + [signature_config_key, signature]\n      )\n\n      verify_signature_value = @hash['verify_signatures'] ? 1 : 0\n      result &&= Overcommit::Utils.execute(\n        %W[git config --local #{verify_signature_config_key} #{verify_signature_value}]\n      )\n\n      unless result.success?\n        raise Overcommit::Exceptions::GitConfigError,\n              \"Unable to write to local repo git config: #{result.stderr}\"\n      end\n    end\n\n    protected\n\n    attr_reader :hash\n\n    private\n\n    def ad_hoc_hook?(hook_context, hook_name)\n      ad_hoc_conf = @hash.fetch(hook_context.hook_class_name) { {} }.fetch(hook_name) { {} }\n\n      # Ad hoc hooks are neither built-in nor have a plugin file written but\n      # still have a `command` specified to be run\n      !built_in_hook?(hook_context, hook_name) &&\n        !plugin_hook?(hook_context, hook_name) &&\n        (ad_hoc_conf['command'] || ad_hoc_conf['required_executable'])\n    end\n\n    def built_in_hook?(hook_context, hook_name)\n      hook_name = Overcommit::Utils.snake_case(hook_name)\n\n      File.exist?(File.join(Overcommit::HOME, 'lib', 'overcommit', 'hook',\n                            hook_context.hook_type_name, \"#{hook_name}.rb\"))\n    end\n\n    def hook_exists?(hook_context, hook_name)\n      built_in_hook?(hook_context, hook_name) ||\n        plugin_hook?(hook_context, hook_name) ||\n        ad_hoc_hook?(hook_context, hook_name)\n    end\n\n    def hook_enabled?(hook_context_or_type, hook_name)\n      hook_type =\n        if hook_context_or_type.is_a?(String)\n          hook_context_or_type\n        else\n          hook_context_or_type.hook_class_name\n        end\n\n      individual_enabled = @hash[hook_type].fetch(hook_name) { {} }['enabled']\n      return individual_enabled unless individual_enabled.nil?\n\n      all_enabled = @hash[hook_type]['ALL']['enabled']\n      return all_enabled unless all_enabled.nil?\n\n      false\n    end\n\n    def smart_merge(parent, child)\n      # Treat the ALL hook specially so that it overrides any configuration\n      # specified by the default configuration.\n      child_all = child['ALL']\n      unless child_all.nil?\n        parent = Hash[parent.collect { |k, v| [k, smart_merge(v, child_all)] }]\n      end\n\n      parent.merge(child) do |_key, old, new|\n        case old\n        when Hash\n          smart_merge(old, new)\n        else\n          new\n        end\n      end\n    end\n\n    # Returns the unique signature of this configuration.\n    #\n    # @return [String]\n    def signature\n      Digest::SHA256.hexdigest(@hash.to_json)\n    end\n\n    # Returns the stored signature of this repo's Overcommit configuration.\n    #\n    # This is intended to be compared against the current signature of this\n    # configuration object.\n    #\n    # @return [String]\n    def stored_signature\n      result = Overcommit::Utils.execute(\n        %w[git config --local --get] + [signature_config_key]\n      )\n\n      if result.status == 1 # Key doesn't exist\n        return ''\n      elsif result.status != 0\n        raise Overcommit::Exceptions::GitConfigError,\n              \"Unable to read from local repo git config: #{result.stderr}\"\n      end\n\n      result.stdout.chomp\n    end\n\n    def signature_config_key\n      'overcommit.configuration.signature'\n    end\n\n    def verify_signature_config_key\n      'overcommit.configuration.verifysignatures'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/configuration_loader.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'yaml'\n\nmodule Overcommit\n  # Manages configuration file loading.\n  class ConfigurationLoader\n    DEFAULT_CONFIG_PATH = File.join(Overcommit::HOME, 'config', 'default.yml')\n\n    class << self\n      # Loads and returns the default configuration.\n      #\n      # @return [Overcommit::Configuration]\n      def default_configuration\n        @default_configuration ||= load_from_file(DEFAULT_CONFIG_PATH, default: true, verify: false)\n      end\n\n      # Loads configuration from file.\n      #\n      # @param file [String] path to file\n      # @param options [Hash]\n      # @option default [Boolean] whether this is the default built-in configuration\n      # @option verify [Boolean] whether to verify the signature of the configuration\n      # @option logger [Overcommit::Logger]\n      # @return [Overcommit::Configuration]\n      def load_from_file(file, options = {})\n        # Psych 4 introduced breaking behavior that doesn't support aliases by\n        # default. Explicitly enable aliases if the option is available.\n        yaml =\n          begin\n            YAML.load_file(file, aliases: true)\n          rescue ArgumentError\n            YAML.load_file(file)\n          end\n\n        hash = yaml ? yaml.to_hash : {}\n        Overcommit::Configuration.new(hash, options)\n      end\n    end\n\n    # Create a configuration loader which writes warnings/errors to the given\n    # {Overcommit::Logger} instance.\n    #\n    # @param logger [Overcommit::Logger]\n    # @param options [Hash]\n    # @option verify [Boolean] whether to verify signatures\n    def initialize(logger, options = {})\n      @log = logger\n      @options = options\n    end\n\n    # Loads and returns the configuration for the repository we're running in.\n    #\n    # @return [Overcommit::Configuration]\n    def load_repo_config\n      overcommit_local_yml = File.join(Overcommit::Utils.repo_root,\n                                       Overcommit::LOCAL_CONFIG_FILE_NAME)\n      overcommit_yml = File.join(Overcommit::Utils.repo_root,\n                                 Overcommit::CONFIG_FILE_NAME)\n\n      if File.exist?(overcommit_local_yml) && File.exist?(overcommit_yml)\n        load_file(overcommit_yml, overcommit_local_yml)\n      elsif File.exist?(overcommit_yml)\n        load_file(overcommit_yml)\n      else\n        self.class.default_configuration\n      end\n    end\n\n    # Loads a configuration, ensuring it extends the default configuration.\n    def load_file(file, local_file = nil)\n      overcommit_config = self.class.load_from_file(file, default: false, logger: @log)\n      l_config = self.class.load_from_file(local_file, default: false, logger: @log) if local_file\n      config = self.class.default_configuration.merge(overcommit_config)\n      config = config.merge(l_config) if l_config\n\n      if @options.fetch(:verify) { config.verify_signatures? }\n        verify_signatures(config)\n      end\n\n      config\n    rescue Overcommit::Exceptions::ConfigurationSignatureChanged\n      raise\n    rescue StandardError => e\n      raise Overcommit::Exceptions::ConfigurationError,\n            \"Unable to load configuration from '#{file}': #{e}\",\n            e.backtrace\n    end\n\n    private\n\n    def verify_signatures(config)\n      if !config.previous_signature?\n        raise Overcommit::Exceptions::ConfigurationSignatureChanged,\n              \"No previously recorded signature for configuration file.\\n\" \\\n              'Run `overcommit --sign` if you trust the hooks in this repository.'\n\n      elsif config.signature_changed?\n        raise Overcommit::Exceptions::ConfigurationSignatureChanged,\n              \"Signature of configuration file has changed!\\n\" \\\n              \"Run `overcommit --sign` once you've verified the configuration changes.\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/configuration_validator.rb",
    "content": "# frozen_string_literal: true\n\n# rubocop:disable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength\nmodule Overcommit\n  # Validates and normalizes a configuration.\n  class ConfigurationValidator\n    # Validates hash for any invalid options, normalizing where possible.\n    #\n    # @param config [Overcommit::Configuration]\n    # @param hash [Hash] hash representation of YAML config\n    # @param options[Hash]\n    # @option default [Boolean] whether hash represents the default built-in config\n    # @option logger [Overcommit::Logger] logger to output warnings to\n    # @return [Hash] validated hash (potentially modified)\n    def validate(config, hash, options)\n      @options = options.dup\n      @log = options[:logger]\n\n      hash = convert_nils_to_empty_hashes(hash)\n      ensure_hook_type_sections_exist(hash)\n      check_hook_name_format(hash)\n      check_hook_env(hash)\n      check_for_missing_enabled_option(hash) unless @options[:default]\n      check_for_too_many_processors(config, hash)\n      check_for_verify_plugin_signatures_option(hash)\n\n      hash\n    end\n\n    private\n\n    # Ensures that keys for all supported hook types exist (PreCommit,\n    # CommitMsg, etc.)\n    def ensure_hook_type_sections_exist(hash)\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hash[hook_type] ||= {}\n        hash[hook_type]['ALL'] ||= {}\n      end\n    end\n\n    # Normalizes `nil` values to empty hashes.\n    #\n    # This is useful for when we want to merge two configuration hashes\n    # together, since it's easier to merge two hashes than to have to check if\n    # one of the values is nil.\n    def convert_nils_to_empty_hashes(hash)\n      hash.each_with_object({}) do |(key, value), h|\n        h[key] =\n          case value\n          when nil  then {}\n          when Hash then convert_nils_to_empty_hashes(value)\n          else\n            value\n          end\n      end\n    end\n\n    def check_hook_env(hash)\n      errors = []\n\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|\n          hook_env = hook_config.fetch('env') { {} }\n\n          unless hook_env.is_a?(Hash)\n            errors << \"#{hook_type}::#{hook_name} has an invalid `env` specified: \" \\\n                      'must be a hash of environment variable name to string value.'\n            next\n          end\n\n          hook_env.each do |var_name, var_value|\n            if var_name.include?('=')\n              errors << \"#{hook_type}::#{hook_name} has an invalid `env` specified: \" \\\n                        \"variable name `#{var_name}` cannot contain `=`.\"\n            end\n\n            unless var_value.nil? || var_value.is_a?(String)\n              errors << \"#{hook_type}::#{hook_name} has an invalid `env` specified: \" \\\n                        \"value of `#{var_name}` must be a string or `nil`, but was \" \\\n                        \"#{var_value.inspect} (#{var_value.class})\"\n            end\n          end\n        end\n      end\n\n      if errors.any?\n        if @log\n          @log.error errors.join(\"\\n\")\n          @log.newline\n        end\n        raise Overcommit::Exceptions::ConfigurationError,\n              'One or more hooks had an invalid `env` configuration option'\n      end\n    end\n\n    # Prints an error message and raises an exception if a hook has an\n    # invalid name, since this can result in strange errors elsewhere.\n    def check_hook_name_format(hash)\n      errors = []\n\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hash.fetch(hook_type) { {} }.each_key do |hook_name|\n          next if hook_name == 'ALL'\n\n          unless hook_name.match?(/\\A[A-Za-z0-9]+\\z/)\n            errors << \"#{hook_type}::#{hook_name} has an invalid name \" \\\n                      \"#{hook_name}. It must contain only alphanumeric \" \\\n                      'characters (no underscores or dashes, etc.)'\n          end\n        end\n      end\n\n      if errors.any?\n        if @log\n          @log.error errors.join(\"\\n\")\n          @log.newline\n        end\n        raise Overcommit::Exceptions::ConfigurationError,\n              'One or more hooks had invalid names'\n      end\n    end\n\n    # Prints a warning if there are any hooks listed in the configuration\n    # without `enabled` explicitly set.\n    def check_for_missing_enabled_option(hash)\n      return unless @log\n\n      any_warnings = false\n\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|\n          next if hook_name == 'ALL'\n\n          if hook_config['enabled'].nil?\n            @log.warning \"#{hook_type}::#{hook_name} hook does not explicitly \" \\\n                         'set `enabled` option in .overcommit.yml'\n            any_warnings = true\n          end\n        end\n      end\n\n      @log.newline if any_warnings\n    end\n\n    # Prints a warning if any hook has a number of processors larger than the\n    # global `concurrency` setting.\n    def check_for_too_many_processors(config, hash)\n      concurrency = config.concurrency\n\n      errors = []\n      Overcommit::Utils.supported_hook_type_classes.each do |hook_type|\n        hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|\n          processors = hook_config.fetch('processors') { 1 }\n          if processors > concurrency\n            errors << \"#{hook_type}::#{hook_name} `processors` value \" \\\n                      \"(#{processors}) is larger than the global `concurrency` \" \\\n                      \"option (#{concurrency})\"\n          end\n        end\n      end\n\n      if errors.any?\n        if @log\n          @log.error errors.join(\"\\n\")\n          @log.newline\n        end\n        raise Overcommit::Exceptions::ConfigurationError,\n              'One or more hooks had invalid `processor` value configured'\n      end\n    end\n\n    # Prints a warning if the `verify_plugin_signatures` option is used instead\n    # of the new `verify_signatures` option.\n    def check_for_verify_plugin_signatures_option(hash)\n      return unless @log\n\n      if hash.key?('verify_plugin_signatures')\n        @log.warning '`verify_plugin_signatures` has been renamed to ' \\\n                     '`verify_signatures`. Defaulting to verifying signatures.'\n        @log.warning \"See change log at #{REPO_URL}/blob/v0.29.0/CHANGELOG.md for details.\"\n        @log.newline\n      end\n    end\n  end\nend\n# rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength\n"
  },
  {
    "path": "lib/overcommit/constants.rb",
    "content": "# frozen_string_literal: true\n\n# Global application constants.\nmodule Overcommit\n  HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze\n  CONFIG_FILE_NAME = '.overcommit.yml'\n  LOCAL_CONFIG_FILE_NAME = '.local-overcommit.yml'\n\n  HOOK_DIRECTORY = File.join(HOME, 'lib', 'overcommit', 'hook').freeze\n\n  REPO_URL = 'https://github.com/sds/overcommit'\n  BUG_REPORT_URL = \"#{REPO_URL}/issues\"\nend\n"
  },
  {
    "path": "lib/overcommit/exceptions.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Exceptions\n  # Base error class.\n  class Error < StandardError; end\n\n  # Raised when a {Configuration} could not be loaded from a file.\n  class ConfigurationError < Error; end\n\n  # Raised when the Overcommit configuration file signature has changed.\n  class ConfigurationSignatureChanged < Error; end\n\n  # Raised when trying to read/write to/from the local repo git config fails.\n  class GitConfigError < Error; end\n\n  # Raised when there was a problem reading submodule information for a repo.\n  class GitSubmoduleError < Error; end\n\n  # Raised when there was a problem reading git revision information with `rev-list`.\n  class GitRevListError < Error; end\n\n  # Raised when a {HookContext} is unable to setup the environment before a run.\n  class HookSetupFailed < Error; end\n\n  # Raised when a {HookContext} is unable to clean the environment after a run.\n  class HookCleanupFailed < Error; end\n\n  # Raised when a hook run was cancelled by the user.\n  class HookCancelled < Error; end\n\n  # Raised when a hook could not be loaded by a {HookRunner}.\n  class HookLoadError < Error; end\n\n  # Raised when a {HookRunner} could not be loaded.\n  class HookContextLoadError < Error; end\n\n  # Raised when a pipe character is used in the `execute` helper, as this was\n  # likely used in error.\n  class InvalidCommandArgs < Error; end\n\n  # Raised when an installation target is not a valid git repository.\n  class InvalidGitRepo < Error; end\n\n  # Raised when a hook was defined incorrectly.\n  class InvalidHookDefinition < Error; end\n\n  # Raised when one or more hook plugin signatures have changed.\n  class InvalidHookSignature < Error; end\n\n  # Raised when there is a problem processing output into {Hook::Messages}s.\n  class MessageProcessingError < Error; end\n\n  # Raised when an installation target already contains non-Overcommit hooks.\n  class PreExistingHooks < Error; end\nend\n"
  },
  {
    "path": "lib/overcommit/git_config.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/utils'\n\nmodule Overcommit\n  # Get configuration options from git\n  module GitConfig\n    module_function\n\n    def comment_character\n      char = `git config --get core.commentchar`.chomp\n      char = '#' if char == ''\n      char\n    end\n\n    def hooks_path\n      path = `git config --get core.hooksPath`.chomp\n      return File.join(Overcommit::Utils.git_dir, 'hooks') if path.empty?\n\n      File.expand_path(path, Dir.pwd)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/git_repo.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'iniparse'\nrequire 'shellwords'\n\nmodule Overcommit\n  # Provide a set of utilities for certain interactions with `git`.\n  module GitRepo\n    module_function\n\n    # Regular expression used to extract diff ranges from hunks of diff output.\n    DIFF_HUNK_REGEX = /\n      ^@@\\s\n      [^\\s]+\\s           # Ignore old file range\n      \\+(\\d+)(?:,(\\d+))? # Extract range of hunk containing start line and number of lines\n      \\s@@.*$\n    /x.freeze\n\n    # Regular expression used to extract information from lines of\n    # `git submodule status` output\n    SUBMODULE_STATUS_REGEX = /\n      ^\\s*(?<prefix>[-+U]?)(?<sha1>\\w+)\n      \\s(?<path>[^\\s]+?)\n      (?:\\s\\((?<describe>.+)\\))?$\n    /x.freeze\n\n    # Struct encapsulating submodule information extracted from the\n    # output of `git submodule status`\n    SubmoduleStatus = Struct.new(:prefix, :sha1, :path, :describe) do\n      # Returns whether the submodule has not been initialized\n      def uninitialized?\n        prefix == '-'\n      end\n\n      # Returns whether the submodule is out of date with the current\n      # index, i.e. its checked-out commit differs from that stored in\n      # the index of the parent repo\n      def outdated?\n        prefix == '+'\n      end\n\n      # Returns whether the submodule reference has a merge conflict\n      def merge_conflict?\n        prefix == 'U'\n      end\n    end\n\n    # Returns a list of SubmoduleStatus objects, one for each submodule in the\n    # parent repository.\n    #\n    # @option options [Boolean] recursive check submodules recursively\n    # @return [Array<SubmoduleStatus>]\n    def submodule_statuses(options = {})\n      flags = '--recursive' if options[:recursive]\n\n      `git submodule status #{flags}`.\n        scan(SUBMODULE_STATUS_REGEX).\n        map do |prefix, sha1, path, describe|\n          SubmoduleStatus.new(prefix, sha1, path, describe)\n        end\n    end\n\n    # Extract the set of modified lines from a given file.\n    #\n    # @param file_path [String]\n    # @param options [Hash]\n    # @return [Set] line numbers that have been modified in file\n    def extract_modified_lines(file_path, options)\n      lines = Set.new\n\n      flags = '--cached' if options[:staged]\n      refs = options[:refs]\n      subcmd = options[:subcmd] || 'diff'\n\n      `git #{subcmd} --no-color --no-ext-diff -U0 #{flags} #{refs} -- \"#{file_path}\"`.\n        scan(DIFF_HUNK_REGEX) do |start_line, lines_added|\n        lines_added = (lines_added || 1).to_i # When blank, one line was added\n        cur_line = start_line.to_i\n\n        lines_added.times do\n          lines.add cur_line\n          cur_line += 1\n        end\n      end\n\n      lines\n    end\n\n    # Returns the names of all files that have been modified compared to HEAD.\n    #\n    # @param options [Hash]\n    # @return [Array<String>] list of absolute file paths\n    def modified_files(options)\n      flags = '--cached' if options[:staged]\n      refs = options[:refs]\n      subcmd = options[:subcmd] || 'diff'\n\n      `git #{subcmd} --name-only -z --diff-filter=ACMR --ignore-submodules=all #{flags} #{refs}`.\n        split(\"\\0\").\n        map(&:strip).\n        reject(&:empty?).\n        map { |relative_file| File.expand_path(relative_file) }\n    end\n\n    # Returns the names of files in the given paths that are tracked by git.\n    #\n    # @param paths [Array<String>] list of paths to check\n    # @option options [String] ref ('HEAD') Git ref to check\n    # @return [Array<String>] list of absolute file paths\n    def list_files(paths = [], options = {})\n      ref = options[:ref] || 'HEAD'\n\n      result = Overcommit::Utils.execute(%W[git ls-tree --name-only #{ref}], args: paths)\n      unless result.success?\n        raise Overcommit::Exceptions::Error,\n              \"Error listing files. EXIT STATUS(es): #{result.statuses}.\\n\" \\\n              \"STDOUT(s): #{result.stdouts}.\\n\" \\\n              \"STDERR(s): #{result.stderrs}.\"\n      end\n\n      result.stdout.split(/\\n/).\n        map { |relative_file| File.expand_path(relative_file) }.\n        reject { |file| File.directory?(file) } # Exclude submodule directories\n    end\n\n    # Returns whether the specified file/path is tracked by this repository.\n    #\n    # @param path [String]\n    # @return [true,false]\n    def tracked?(path)\n      Overcommit::Utils.execute(%W[git ls-files #{path} --error-unmatch]).success?\n    end\n\n    # Returns the names of all files that are tracked by git.\n    #\n    # @return [Array<String>] list of absolute file paths\n    def all_files\n      `git ls-files`.\n        split(/\\n/).\n        map { |relative_file| File.expand_path(relative_file) }.\n        reject { |file| File.directory?(file) } # Exclude submodule directories\n    end\n\n    # Returns whether the current git branch is empty (has no commits).\n    # @return [true,false]\n    def initial_commit?\n      !Overcommit::Utils.execute(%w[git rev-parse HEAD]).success?\n    end\n\n    # Store any relevant files that are present when repo is in the middle of a\n    # merge.\n    #\n    # Restored via [#restore_merge_state].\n    def store_merge_state\n      merge_head = `git rev-parse MERGE_HEAD 2> #{File::NULL}`.chomp\n\n      # Store the merge state if we're in the middle of resolving a merge\n      # conflict. This is necessary since stashing removes the merge state.\n      if merge_head != 'MERGE_HEAD'\n        @merge_head = merge_head\n      end\n\n      merge_msg_file = File.expand_path('MERGE_MSG', Overcommit::Utils.git_dir)\n      @merge_msg = File.open(merge_msg_file).read if File.exist?(merge_msg_file)\n    end\n\n    # Store any relevant files that are present when repo is in the middle of a\n    # cherry-pick.\n    #\n    # Restored via [#restore_cherry_pick_state].\n    def store_cherry_pick_state\n      cherry_head = `git rev-parse CHERRY_PICK_HEAD 2> #{File::NULL}`.chomp\n\n      # Store the merge state if we're in the middle of resolving a merge\n      # conflict. This is necessary since stashing removes the merge state.\n      if cherry_head != 'CHERRY_PICK_HEAD'\n        @cherry_head = cherry_head\n      end\n    end\n\n    # Restore any relevant files that were present when repo was in the middle\n    # of a merge.\n    def restore_merge_state\n      if @merge_head\n        FileUtils.touch(File.expand_path('MERGE_MODE', Overcommit::Utils.git_dir))\n\n        File.open(File.expand_path('MERGE_HEAD', Overcommit::Utils.git_dir), 'w') do |f|\n          f.write(@merge_head)\n        end\n        @merge_head = nil\n      end\n\n      if @merge_msg\n        File.open(File.expand_path('MERGE_MSG', Overcommit::Utils.git_dir), 'w') do |f|\n          f.write(\"#{@merge_msg}\\n\")\n        end\n        @merge_msg = nil\n      end\n    end\n\n    # Restore any relevant files that were present when repo was in the middle\n    # of a cherry-pick.\n    def restore_cherry_pick_state\n      if @cherry_head\n        File.open(File.expand_path('CHERRY_PICK_HEAD',\n                                   Overcommit::Utils.git_dir), 'w') do |f|\n          f.write(@cherry_head)\n        end\n        @cherry_head = nil\n      end\n    end\n\n    # Contains information about a registered submodule.\n    Submodule = Struct.new(:path, :url)\n\n    # Returns the submodules that have been staged for removal.\n    #\n    # `git` has an unexpected behavior where removing a submodule without\n    # committing (i.e. such that the submodule directory is removed and the\n    # changes to the index are staged) and then doing a hard reset results in\n    # the index being wiped but the empty directory of the once existent\n    # submodule being restored (but with no content).\n    #\n    # This prevents restoration of the stash of the submodule index changes,\n    # which breaks pre-commit hook restorations of the working index.\n    #\n    # Thus we expose this helper so the restoration code can manually delete the\n    # directory.\n    #\n    # @raise [Overcommit::Exceptions::GitSubmoduleError] when\n    def staged_submodule_removals\n      # There were no submodules before, so none could have been removed\n      return [] if `git ls-files .gitmodules`.empty?\n\n      previous = submodules(ref: 'HEAD')\n      current = submodules\n\n      previous - current\n    end\n\n    # Returns the current set of registered submodules.\n    #\n    # @param options [Hash]\n    # @return [Array<Overcommit::GitRepo::Submodule>]\n    def submodules(options = {})\n      ref = options[:ref]\n\n      modules = []\n      IniParse.parse(`git show #{ref}:.gitmodules 2> #{File::NULL}`).each do |section|\n        # git < 1.8.5 does not update the .gitmodules file with submodule\n        # changes, so when we are looking at the current state of the work tree,\n        # we need to check if the submodule actually exists via another method,\n        # since the .gitmodules file we parsed does not represent reality.\n        if ref.nil? && GIT_VERSION < '1.8.5'\n          result = Overcommit::Utils.execute(%W[\n            git submodule status #{section['path']}\n          ])\n          next unless result.success?\n        end\n\n        modules << Submodule.new(section['path'], section['url'])\n      end\n\n      modules\n    rescue IniParse::IniParseError => e\n      raise Overcommit::Exceptions::GitSubmoduleError,\n            \"Unable to read submodule information from #{ref}:.gitmodules file: #{e.message}\"\n    end\n\n    # Returns the names of all branches containing the given commit.\n    #\n    # @param commit_ref [String] git tree ref that resolves to a commit\n    # @return [Array<String>] list of branches containing the given commit\n    def branches_containing_commit(commit_ref)\n      `git branch --column=dense --contains #{commit_ref}`.\n        sub(/\\((HEAD )?detached (from|at) .*?\\)/, ''). # ignore detached HEAD\n        split(/\\s+/).\n        reject { |s| s.empty? || s == '*' }\n    end\n\n    # Returns the name of the currently checked out branch.\n    # @return [String]\n    def current_branch\n      `git symbolic-ref --short -q HEAD`.chomp\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/git_version.rb",
    "content": "# frozen_string_literal: true\n\n# Returns the version of the available git binary.\n#\n# This is intended to be used to conveniently execute code based on a specific\n# git version. Simply compare to a version string:\n#\n# @example\n#   if GIT_VERSION <= '1.8.5'\n#     ...\n#   end\nmodule Overcommit\n  GIT_VERSION = begin\n    version = `git --version`.chomp[/\\d+(\\.\\d+)+/, 0]\n    Overcommit::Utils::Version.new(version)\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\nrequire 'overcommit/message_processor'\n\n# Container for top-level hook-related classes and constants.\nmodule Overcommit::Hook\n  # Helper containing metadata about error/warning messages returned by hooks.\n  Message = Struct.new(:type, :file, :line, :content) do\n    def to_s\n      content\n    end\n  end\n\n  # Possible types of messages.\n  MESSAGE_TYPES = [:error, :warning].freeze\n\n  # Functionality common to all hooks.\n  class Base # rubocop:disable Metrics/ClassLength\n    extend Forwardable\n\n    def_delegators :@context, :all_files, :modified_files\n    attr_reader :config\n\n    # @param config [Overcommit::Configuration]\n    # @param context [Overcommit::HookContext]\n    def initialize(config, context)\n      @config = config.for_hook(self)\n      @context = context\n    end\n\n    # Runs the hook.\n    def run\n      raise NotImplementedError, 'Hook must define `run`'\n    end\n\n    # Runs the hook and transforms the status returned based on the hook's\n    # configuration.\n    #\n    # Poorly named because we already have a bunch of hooks in the wild that\n    # implement `#run`, and we needed a wrapper step to transform the status\n    # based on any custom configuration.\n    def run_and_transform\n      if output = check_for_requirements\n        status = :fail\n      else\n        result = Overcommit::Utils.with_environment(@config.fetch('env') { {} }) { run }\n        status, output = process_hook_return_value(result)\n      end\n\n      [transform_status(status), output]\n    end\n\n    def name\n      self.class.name.split('::').last\n    end\n\n    def description\n      @config['description'] || \"Run #{name}\"\n    end\n\n    def required?\n      @config['required']\n    end\n\n    def parallelize?\n      @config['parallelize'] != false\n    end\n\n    def processors\n      @config.fetch('processors') { 1 }\n    end\n\n    def quiet?\n      @config['quiet']\n    end\n\n    def enabled?\n      @config['enabled'] != false\n    end\n\n    def excluded?\n      exclude_branches.any? { |p| File.fnmatch(p, current_branch) }\n    end\n\n    def skip?\n      @config['skip'] ||\n        (@config['skip_if'] ? execute(@config['skip_if']).success? : false)\n    end\n\n    def run?\n      enabled? &&\n      !excluded? &&\n        !(@config['requires_files'] && applicable_files.empty?)\n    end\n\n    def in_path?(cmd)\n      Overcommit::Utils.in_path?(cmd)\n    end\n\n    # Execute a command in a separate process.\n    #\n    # If `splittable_args` is specified, ensures that those arguments are\n    # concatenated onto the end of the `cmd` arguments, but split up so that the\n    # operating system's maximum command length is not exceeded. This is useful\n    # for splitting up long file lists.\n    #\n    # @param cmd [Array<String>] command arguments\n    # @param options [Hash]\n    # @option options [Array<String>] :args arguments that can be split up over\n    #   multiple invocations (usually a list of files)\n    # @option options [String] :input string to pass to process' standard input\n    #   stream\n    # @return [#status,#stdout,#stderr] struct containing result of invocation\n    def execute(cmd, options = {})\n      Overcommit::Utils.execute(cmd, options)\n    end\n\n    def execute_in_background(cmd)\n      Overcommit::Utils.execute_in_background(cmd)\n    end\n\n    def required_executable\n      @config['required_executable']\n    end\n\n    def required_libraries\n      Array(@config['required_library'] || @config['required_libraries'])\n    end\n\n    # Return command to execute for this hook.\n    #\n    # This is intended to be configurable so hooks can prefix their commands\n    # with `bundle exec` or similar. It will appends the command line flags\n    # specified by the `flags` option after.\n    #\n    # Note that any files intended to be passed must be handled by the hook\n    # itself.\n    #\n    # @return [Array<String>]\n    def command\n      Array(@config['command'] || required_executable) + flags\n    end\n\n    # Return command line flags to be passed to the command.\n    #\n    # This excludes the list of files, as that must be handled by the hook\n    # itself.\n    #\n    # The intention here is to provide flexibility for when a tool\n    # removes/renames its flags. Rather than wait for Overcommit to update the\n    # flags it uses, you can update your configuration to use the new flags\n    # right away without being blocked.\n    #\n    # Also note that any flags containing dynamic content must be passed in the\n    # hook's {#run} method.\n    #\n    # @return [Array<String>]\n    def flags\n      Array(@config['flags'])\n    end\n\n    # Gets a list of staged files that apply to this hook based on its\n    # configured `include` and `exclude` lists.\n    def applicable_files\n      @applicable_files ||= select_applicable(modified_files)\n    end\n\n    # Gets a list of all files that apply to this hook based on its\n    # configured `include` and `exclude` lists.\n    def included_files\n      @included_files ||= select_applicable(all_files)\n    end\n\n    private\n\n    def select_applicable(list)\n      list.select { |file| applicable_file?(file) }.sort\n    end\n\n    def applicable_file?(file)\n      includes = Array(@config['include']).flatten.map do |glob|\n        Overcommit::Utils.convert_glob_to_absolute(glob)\n      end\n\n      included = includes.empty? || includes.any? do |glob|\n        Overcommit::Utils.matches_path?(glob, file)\n      end\n\n      excludes = Array(@config['exclude']).flatten.map do |glob|\n        Overcommit::Utils.convert_glob_to_absolute(glob)\n      end\n\n      excluded = excludes.any? do |glob|\n        Overcommit::Utils.matches_path?(glob, file)\n      end\n\n      included && !excluded\n    end\n\n    # Check for any required executables or libraries.\n    #\n    # Returns output if any requirements are not met.\n    def check_for_requirements\n      check_for_executable || check_for_libraries\n    end\n\n    # If the hook defines a required executable, check if it's in the path and\n    # display the install command if one exists.\n    def check_for_executable\n      return unless required_executable && !in_path?(required_executable)\n\n      \"'#{required_executable}' is not installed, not in your PATH, \" \\\n      \"or does not have execute permissions#{install_command_prompt}\"\n    end\n\n    def install_command_prompt\n      if install_command = @config['install_command']\n        \"\\nInstall it by running: #{install_command}\"\n      else\n        ''\n      end\n    end\n\n    # If the hook defines required library paths that it wants to load, attempt\n    # to load them.\n    def check_for_libraries\n      output = []\n\n      required_libraries.each do |library|\n        require library\n      rescue LoadError\n        install_command = @config['install_command']\n        install_command = \" -- install via #{install_command}\" if install_command\n\n        output << \"Unable to load '#{library}'#{install_command}\"\n      end\n\n      return if output.empty?\n\n      output.join(\"\\n\")\n    end\n\n    # Converts the hook's return value into a canonical form of a tuple\n    # containing status (pass/warn/fail) and output.\n    #\n    # This is intended to support various shortcuts for writing hooks so that\n    # hook authors don't need to work with {Overcommit::Hook::Message} objects\n    # for simple pass/fail hooks. It also saves you from needing to manually\n    # encode logic like \"if there are errors, fail; if there are warnings, warn,\n    # otherwise pass.\" by simply returning an array of\n    # {Overcommit::Hook::Message} objects.\n    #\n    # @param hook_return_value [Symbol, Array<Symbol,String>, Array<Message>]\n    # @return [Array<Symbol,String>] tuple of status and output\n    def process_hook_return_value(hook_return_value)\n      if hook_return_value.is_a?(Array) &&\n         (hook_return_value.first.is_a?(Message) || hook_return_value.empty?)\n        # Process messages into a status and output\n        Overcommit::MessageProcessor.new(\n          self,\n          @config['problem_on_unmodified_line'],\n        ).hook_result(hook_return_value)\n      else\n        # Otherwise return as-is\n        hook_return_value\n      end\n    end\n\n    # Transforms the hook's status based on custom configuration.\n    #\n    # This allows users to change failures into warnings, or vice versa.\n    def transform_status(status)\n      case status\n      when :fail\n        @config.fetch('on_fail') { :fail }.to_sym\n      when :warn\n        @config.fetch('on_warn') { :warn }.to_sym\n      else\n        status\n      end\n    end\n\n    def exclude_branches\n      @config['exclude_branches'] || []\n    end\n\n    def current_branch\n      @current_branch ||= Overcommit::GitRepo.current_branch\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::CommitMsg\n  # Functionality common to all commit-msg hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :empty_message?, :commit_message,\n                   :update_commit_message, :commit_message_lines,\n                   :commit_message_file, :modified_lines_in_file\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/capitalized_subject.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures commit message subject lines start with a capital letter.\n  class CapitalizedSubject < Base\n    def run\n      return :pass if empty_message?\n\n      # Git treats the first non-empty line as the subject\n      subject = commit_message_lines.find { |line| !line.strip.empty? }.to_s\n      first_letter = subject.match(/^[[:punct:]]*(.)/)[1]\n      unless special_prefix?(subject) || first_letter =~ /[[:upper:]]/\n        return :warn, 'Subject should start with a capital letter'\n      end\n\n      :pass\n    end\n\n    private\n\n    def special_prefix?(subject)\n      subject =~ /^(fixup|squash)!/\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/empty_message.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Checks that the commit message is not empty\n  class EmptyMessage < Base\n    def run\n      return :pass unless empty_message?\n\n      [:fail, 'Commit message should not be empty']\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/gerrit_change_id.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures a Gerrit Change-Id line is included in the commit message.\n  #\n  # It may seem odd to do this here instead of in a prepare-commit-msg hook, but\n  # the reality is that if you want to _ensure_ the Change-Id is included then\n  # you need to do it in a commit-msg hook. This is because the user could still\n  # edit the message after a prepare-commit-msg hook was run.\n  #\n  # @see https://code.google.com/p/gerrit/\n  class GerritChangeId < Base\n    SCRIPT_LOCATION = Overcommit::Utils.script_path('gerrit-change-id')\n\n    def run\n      result = execute(['sh', SCRIPT_LOCATION, commit_message_file])\n      return :pass if result.success?\n\n      [:fail, result.stdout]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/hard_tabs.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Checks for hard tabs in commit messages.\n  class HardTabs < Base\n    def run\n      return :pass if empty_message?\n\n      # Catches hard tabs entered by the user (not auto-generated)\n      if commit_message.index(/\\t/)\n        return :warn, \"Don't use hard tabs in commit messages\"\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/message_format.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures the commit message follows a specific format.\n  class MessageFormat < Base\n    def run\n      error_msg = validate_pattern(commit_message_lines.join(\"\\n\"))\n      return :fail, error_msg if error_msg\n\n      :pass\n    end\n\n    private\n\n    def validate_pattern(message)\n      pattern = config['pattern']\n      return if pattern.empty?\n\n      expected_pattern_message = config['expected_pattern_message']\n      sample_message = config['sample_message']\n\n      unless message.match?(/#{pattern}/m)\n        [\n          'Commit message pattern mismatch.',\n          \"Expected : #{expected_pattern_message}\",\n          \"Sample : #{sample_message}\"\n        ].join(\"\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/russian_novel.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Checks for long commit messages (not good or bad--just fun to point out)\n  class RussianNovel < Base\n    RUSSIAN_NOVEL_LENGTH = 30\n\n    def run\n      if commit_message_lines.length >= RUSSIAN_NOVEL_LENGTH\n        return :warn, 'You seem to have authored a Russian novel; congratulations!'\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/single_line_subject.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures commit message subject lines are followed by a blank line.\n  class SingleLineSubject < Base\n    def run\n      return :pass if empty_message?\n\n      unless commit_message_lines[1].to_s.strip.empty?\n        return :warn, 'Subject should be one line and followed by a blank line'\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/spell_check.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'tempfile'\n\nmodule Overcommit::Hook::CommitMsg\n  # Checks the commit message for potential misspellings with `hunspell`.\n  #\n  # @see http://hunspell.sourceforge.net/\n  class SpellCheck < Base\n    Misspelling = Struct.new(:word, :suggestions)\n\n    MISSPELLING_REGEX = /^[&#]\\s(?<word>\\w+)(?:.+?:\\s(?<suggestions>.*))?/.freeze\n\n    def run\n      result = execute(command + [uncommented_commit_msg_file])\n      return [:fail, \"Error running spellcheck: #{result.stderr.chomp}\"] unless result.success?\n\n      misspellings = parse_misspellings(result.stdout)\n      return :pass if misspellings.empty?\n\n      messages = misspellings.map do |misspelled|\n        msg = \"Potential misspelling: #{misspelled.word}.\"\n        msg += \" Suggestions: #{misspelled.suggestions}\" unless misspelled.suggestions.nil?\n        msg\n      end\n\n      [:warn, messages.join(\"\\n\")]\n    end\n\n    private\n\n    def uncommented_commit_msg_file\n      ::Tempfile.open('commit-msg') do |file|\n        file.write(commit_message)\n        file.path\n      end\n    end\n\n    def parse_misspellings(output)\n      output.scan(MISSPELLING_REGEX).map do |word, suggestions|\n        Misspelling.new(word, suggestions)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/text_width.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures the number of columns the subject and commit message lines occupy is\n  # under the preferred limits.\n  class TextWidth < Base\n    def run\n      return :pass if empty_message?\n\n      @errors = []\n\n      find_errors_in_subject(commit_message_lines.first.chomp)\n      find_errors_in_body(commit_message_lines)\n\n      return :warn, @errors.join(\"\\n\") if @errors.any?\n\n      :pass\n    end\n\n    private\n\n    def find_errors_in_subject(subject)\n      max_subject_width =\n        config['max_subject_width'] +\n        special_prefix_length(subject)\n\n      if subject.length > max_subject_width\n        @errors << \"Commit message subject must be <= #{max_subject_width} characters\"\n        return\n      end\n\n      min_subject_width = config['min_subject_width']\n      if subject.length < min_subject_width\n        @errors << \"Commit message subject must be >= #{min_subject_width} characters\"\n        nil\n      end\n    end\n\n    def find_errors_in_body(lines)\n      return unless lines.count > 2\n\n      max_body_width = config['max_body_width']\n\n      lines[2..].each_with_index do |line, index|\n        if line.chomp.size > max_body_width\n          @errors << \"Line #{index + 3} of commit message has > \" \\\n                    \"#{max_body_width} characters\"\n        end\n      end\n    end\n\n    def special_prefix_length(subject)\n      subject.match(/^(fixup|squash)! /) { |match| match[0].length } || 0\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/commit_msg/trailing_period.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::CommitMsg\n  # Ensures commit message subject lines do not have a trailing period\n  class TrailingPeriod < Base\n    def run\n      return :pass if empty_message?\n\n      if commit_message_lines.first.rstrip.end_with?('.')\n        return :warn, 'Please omit trailing period from commit message subject'\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PostCheckout\n  # Functionality common to all post-checkout hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context,\n                   :previous_head, :new_head, :branch_checkout?, :file_checkout?\n\n    def skip_file_checkout?\n      @config['skip_file_checkout'] != false\n    end\n\n    def enabled?\n      return false if file_checkout? && skip_file_checkout?\n\n      super\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/bower_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bower_install'\n\nmodule Overcommit::Hook::PostCheckout\n  # Runs `bower install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BowerInstall\n  class BowerInstall < Base\n    include Overcommit::Hook::Shared::BowerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/bundle_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bundle_install'\n\nmodule Overcommit::Hook::PostCheckout\n  # Runs `bundle install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BundleInstall\n  class BundleInstall < Base\n    include Overcommit::Hook::Shared::BundleInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/composer_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/composer_install'\n\nmodule Overcommit::Hook::PostCheckout\n  # Runs `composer install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::ComposerInstall\n  class ComposerInstall < Base\n    include Overcommit::Hook::Shared::ComposerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/index_tags.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/index_tags'\n\nmodule Overcommit::Hook::PostCheckout\n  # Updates ctags index for all source code in the repository.\n  #\n  # @see Overcommit::Hook::Shared::IndexTags\n  class IndexTags < Base\n    include Overcommit::Hook::Shared::IndexTags\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/npm_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/npm_install'\n\nmodule Overcommit::Hook::PostCheckout\n  # Runs `npm install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::NpmInstall\n  class NpmInstall < Base\n    include Overcommit::Hook::Shared::NpmInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/submodule_status.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/submodule_status'\n\nmodule Overcommit::Hook::PostCheckout\n  # Checks the status of submodules in the current repository and\n  # notifies the user if any are uninitialized, out of date with\n  # the current index, or contain merge conflicts.\n  class SubmoduleStatus < Base\n    include Overcommit::Hook::Shared::SubmoduleStatus\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_checkout/yarn_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/yarn_install'\n\nmodule Overcommit::Hook::PostCheckout\n  # Runs `yarn install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::YarnInstall\n  class YarnInstall < Base\n    include Overcommit::Hook::Shared::YarnInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PostCommit\n  # Functionality common to all post-commit hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :modified_lines_in_file, :initial_commit?\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/bower_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bower_install'\n\nmodule Overcommit::Hook::PostCommit\n  # Runs `bower install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BowerInstall\n  class BowerInstall < Base\n    include Overcommit::Hook::Shared::BowerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/bundle_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bundle_install'\n\nmodule Overcommit::Hook::PostCommit\n  # Runs `bundle install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BundleInstall\n  class BundleInstall < Base\n    include Overcommit::Hook::Shared::BundleInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/commitplease.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PostCommit\n  # Check that a commit message conforms to a certain style\n  #\n  # @see https://www.npmjs.com/package/commitplease\n  class Commitplease < Base\n    def run\n      result = execute(command)\n      output = result.stderr\n      return :pass if result.success? && output.empty?\n\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/composer_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/composer_install'\n\nmodule Overcommit::Hook::PostCommit\n  # Runs `composer install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::ComposerInstall\n  class ComposerInstall < Base\n    include Overcommit::Hook::Shared::ComposerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/git_guilt.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PostCommit\n  # Calculates the change in blame since the last revision.\n  #\n  # @see https://www.npmjs.com/package/git-guilt\n  class GitGuilt < Base\n    PLUS_MINUS_REGEX = /^(.*?)(?:(\\++)|(-+))$/.freeze\n    GREEN = 32\n    RED = 31\n\n    def run\n      return :pass if initial_commit?\n\n      result = execute(command)\n      return :fail, result.stderr unless result.success?\n\n      return :pass if result.stdout.strip.empty?\n\n      output = []\n      result.stdout.scan(PLUS_MINUS_REGEX) do |user, plus, minus|\n        plus = color(GREEN, plus)\n        minus = color(RED, minus)\n        output << \"#{user}#{plus}#{minus}\"\n      end\n\n      [:warn, output.join(\"\\n\")]\n    end\n\n    private\n\n    # Returns text wrapped in ANSI escape code necessary to produce a given\n    # color/text display.\n    #\n    # Taken from Overcommit::Logger as a temporary workaround.\n    # TODO: expose logger instance to hooks for colorized output\n    #\n    # @param code [String] ANSI escape code, e.g. '1;33' for \"bold yellow\"\n    # @param str [String] string to wrap\n    def color(code, str)\n      STDOUT.tty? ? \"\\033[#{code}m#{str}\\033[0m\" : str\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/index_tags.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/index_tags'\n\nmodule Overcommit::Hook::PostCommit\n  # Updates ctags index for all source code in the repository.\n  #\n  # @see Overcommit::Hook::Shared::IndexTags\n  class IndexTags < Base\n    include Overcommit::Hook::Shared::IndexTags\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/npm_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/npm_install'\n\nmodule Overcommit::Hook::PostCommit\n  # Runs `npm install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::NpmInstall\n  class NpmInstall < Base\n    include Overcommit::Hook::Shared::NpmInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/submodule_status.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/submodule_status'\n\nmodule Overcommit::Hook::PostCommit\n  # Checks the status of submodules in the current repository and\n  # notifies the user if any are uninitialized, out of date with\n  # the current index, or contain merge conflicts.\n  class SubmoduleStatus < Base\n    include Overcommit::Hook::Shared::SubmoduleStatus\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_commit/yarn_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/yarn_install'\n\nmodule Overcommit::Hook::PostCommit\n  # Runs `yarn install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::YarnInstall\n  class YarnInstall < Base\n    include Overcommit::Hook::Shared::YarnInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PostMerge\n  # Functionality common to all post-merge hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :modified_lines_in_file, :squash?, :merge_commit?\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/bower_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bower_install'\n\nmodule Overcommit::Hook::PostMerge\n  # Runs `bower install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BowerInstall\n  class BowerInstall < Base\n    include Overcommit::Hook::Shared::BowerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/bundle_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bundle_install'\n\nmodule Overcommit::Hook::PostMerge\n  # Runs `bundle install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BundleInstall\n  class BundleInstall < Base\n    include Overcommit::Hook::Shared::BundleInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/composer_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/composer_install'\n\nmodule Overcommit::Hook::PostMerge\n  # Runs `composer install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::ComposerInstall\n  class ComposerInstall < Base\n    include Overcommit::Hook::Shared::ComposerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/index_tags.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/index_tags'\n\nmodule Overcommit::Hook::PostMerge\n  # Updates ctags index for all source code in the repository.\n  #\n  # @see Overcommit::Hook::Shared::IndexTags\n  class IndexTags < Base\n    include Overcommit::Hook::Shared::IndexTags\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/npm_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/npm_install'\n\nmodule Overcommit::Hook::PostMerge\n  # Runs `npm install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::NpmInstall\n  class NpmInstall < Base\n    include Overcommit::Hook::Shared::NpmInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/submodule_status.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/submodule_status'\n\nmodule Overcommit::Hook::PostMerge\n  # Checks the status of submodules in the current repository and\n  # notifies the user if any are uninitialized, out of date with\n  # the current index, or contain merge conflicts.\n  class SubmoduleStatus < Base\n    include Overcommit::Hook::Shared::SubmoduleStatus\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_merge/yarn_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/yarn_install'\n\nmodule Overcommit::Hook::PostMerge\n  # Runs `yarn install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::YarnInstall\n  class YarnInstall < Base\n    include Overcommit::Hook::Shared::YarnInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PostRewrite\n  # Functionality common to all post-rewrite hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :amend?, :rebase?, :rewritten_commits\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/bower_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bower_install'\n\nmodule Overcommit::Hook::PostRewrite\n  # Runs `bower install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BowerInstall\n  class BowerInstall < Base\n    include Overcommit::Hook::Shared::BowerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/bundle_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/bundle_install'\n\nmodule Overcommit::Hook::PostRewrite\n  # Runs `bundle install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::BundleInstall\n  class BundleInstall < Base\n    include Overcommit::Hook::Shared::BundleInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/composer_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/composer_install'\n\nmodule Overcommit::Hook::PostRewrite\n  # Runs `composer install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::ComposerInstall\n  class ComposerInstall < Base\n    include Overcommit::Hook::Shared::ComposerInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/index_tags.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/index_tags'\n\nmodule Overcommit::Hook::PostRewrite\n  # Updates ctags index for all source code in the repository.\n  #\n  # @see Overcommit::Hook::Shared::IndexTags\n  class IndexTags < Base\n    include Overcommit::Hook::Shared::IndexTags\n\n    def run\n      # Ignore unless this is a rebase (amends are covered by post-commit hook)\n      return :pass unless rebase?\n\n      super\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/npm_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/npm_install'\n\nmodule Overcommit::Hook::PostRewrite\n  # Runs `npm install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::NpmInstall\n  class NpmInstall < Base\n    include Overcommit::Hook::Shared::NpmInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/submodule_status.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/submodule_status'\n\nmodule Overcommit::Hook::PostRewrite\n  # Checks the status of submodules in the current repository and\n  # notifies the user if any are uninitialized, out of date with\n  # the current index, or contain merge conflicts.\n  class SubmoduleStatus < Base\n    include Overcommit::Hook::Shared::SubmoduleStatus\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/post_rewrite/yarn_install.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/yarn_install'\n\nmodule Overcommit::Hook::PostRewrite\n  # Runs `yarn install` when a change is detected in the repository's\n  # dependencies.\n  #\n  # @see Overcommit::Hook::Shared::YarnInstall\n  class YarnInstall < Base\n    include Overcommit::Hook::Shared::YarnInstall\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/author_email.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks the format of an author's email address.\n  class AuthorEmail < Base\n    def run\n      email =\n        if ENV.key?('GIT_AUTHOR_EMAIL')\n          ENV['GIT_AUTHOR_EMAIL']\n        else\n          result = execute(%w[git config --get user.email])\n          result.stdout.chomp\n        end\n\n      unless email.match?(/#{config['pattern']}/)\n        return :fail,\n               \"Author has an invalid email address: '#{email}'\\n\" \\\n               'Set your email with ' \\\n               '`git config --global user.email your_email@example.com` ' \\\n               'or via the GIT_AUTHOR_EMAIL environment variable'\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/author_name.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Ensures that a commit author has a name with at least first and last names.\n  class AuthorName < Base\n    def run\n      name =\n        if ENV.key?('GIT_AUTHOR_NAME')\n          ENV['GIT_AUTHOR_NAME']\n        else\n          result = execute(%w[git config --get user.name])\n          result.stdout.chomp\n        end\n\n      if name.empty?\n        return :fail,\n               \"Author name must be non-0 in length.\\n\" \\\n               'Set your name with `git config --global user.name \"Your Name\"` ' \\\n               'or via the GIT_AUTHOR_NAME environment variable'\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\nrequire 'overcommit/utils/messages_utils'\n\nmodule Overcommit::Hook::PreCommit\n  # Functionality common to all pre-commit hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :modified_lines_in_file, :amendment?, :initial_commit?\n\n    private\n\n    def extract_messages(*args)\n      Overcommit::Utils::MessagesUtils.extract_messages(*args)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/berksfile_check.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check if local Berksfile.lock matches Berksfile when either changes, unless\n  # Berksfile.lock is ignored by git.\n  #\n  # @see http://berkshelf.com/\n  class BerksfileCheck < Base\n    LOCK_FILE = 'Berksfile.lock'\n\n    def run\n      # Ignore if Berksfile.lock is not tracked by git\n      ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split(\"\\n\")\n      return :pass if ignored_files.include?(LOCK_FILE)\n\n      result = execute(command)\n      unless result.success?\n        return :fail, result.stderr\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/broken_symlinks.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for broken symlinks.\n  class BrokenSymlinks < Base\n    def run\n      broken_symlinks = applicable_files.\n        select { |file| Overcommit::Utils.broken_symlink?(file) }\n\n      if broken_symlinks.any?\n        return :fail, \"Broken symlinks detected:\\n#{broken_symlinks.join(\"\\n\")}\"\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/bundle_audit.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for vulnerable versions of gems in Gemfile.lock.\n  #\n  # @see https://github.com/rubysec/bundler-audit\n  class BundleAudit < Base\n    LOCK_FILE = 'Gemfile.lock'\n\n    def run\n      # Ignore if Gemfile.lock is not tracked by git\n      ignored_files = execute(%W[git ls-files -o -i --exclude-standard -- #{LOCK_FILE}]).\n                      stdout.split(\"\\n\")\n      return :pass if ignored_files.include?(LOCK_FILE)\n\n      result = execute(command)\n      if result.success?\n        :pass\n      else\n        [:warn, result.stdout]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/bundle_check.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check if local Gemfile.lock matches Gemfile when either changes, unless\n  # Gemfile.lock is ignored by git.\n  #\n  # @see http://bundler.io/\n  class BundleCheck < Base\n    LOCK_FILE = File.basename(ENV['BUNDLE_GEMFILE'] || 'Gemfile') + '.lock'\n\n    def run\n      # Ignore if Gemfile.lock is not tracked by git\n      ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split(\"\\n\")\n      return :pass if ignored_files.include?(LOCK_FILE)\n\n      previous_lockfile = File.read(LOCK_FILE) if File.exist?(LOCK_FILE)\n\n      result = execute(command)\n      unless result.success?\n        return :fail, result.stdout\n      end\n\n      new_lockfile = File.read(LOCK_FILE) if File.exist?(LOCK_FILE)\n      if previous_lockfile != new_lockfile\n        return :fail, \"#{LOCK_FILE} is not up-to-date -- run \\\n        `#{command.join(' ')}` or add the Gemfile and/or Gemfile.lock\".squeeze\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/bundle_outdated.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check if any gems in Gemfile.lock have newer versions, unless the\n  # Gemfile.lock is ignored by Git.\n  #\n  # @see http://bundler.io/bundle_outdated.html\n  class BundleOutdated < Base\n    LOCK_FILE = 'Gemfile.lock'\n\n    def run\n      # Ignore if Gemfile.lock is not tracked by git\n      ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split(\"\\n\")\n      return :pass if ignored_files.include?(LOCK_FILE)\n\n      result = execute(command)\n      warn_msgs = result.stdout.split(\"\\n\").\n                         reject { |str| str.strip.empty? }.\n                         reject { |str| (str.strip =~ /^(\\[|\\()?warning|deprecation/i) }\n      warnings = warn_msgs.map { |msg| Overcommit::Hook::Message.new(:warning, nil, nil, msg) }\n\n      warnings.empty? ? :pass : warnings\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/case_conflicts.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for files that would conflict in case-insensitive filesystems\n  # Adapted from https://github.com/pre-commit/pre-commit-hooks\n  class CaseConflicts < Base\n    def run\n      repo_files = Set.new(applicable_files)\n\n      unless Overcommit::GitRepo.initial_commit?\n        paths = repo_files.map { |file| File.dirname(file) + File::SEPARATOR }.uniq\n        repo_files += Overcommit::GitRepo.list_files(paths)\n      end\n\n      conflict_hash = repo_files.classify(&:downcase).\n        select { |_, files| files.size > 1 }\n      conflict_files = applicable_files.\n        select { |file| conflict_hash.include?(file.downcase) }\n\n      conflict_files.map do |file|\n        conflicts = conflict_hash[file.downcase].map { |f| File.basename(f) }\n        msg = \"Conflict detected for case-insensitive file systems: #{conflicts.join(', ')}\"\n        Overcommit::Hook::Message.new(:error, file, nil, msg)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/chamber_compare.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `chamber compare` against a configurable set of namespaces.\n  #\n  # @see https://github.com/thekompanee/chamber/wiki/Git-Commit-Hooks#chamber-compare-pre-commit-hook\n  # rubocop:disable Metrics/MethodLength\n  class ChamberCompare < Base\n    def run\n      config['namespaces'].each_index do |index|\n        first  = config['namespaces'][index]\n        second = config['namespaces'][index + 1]\n\n        next unless second\n\n        result = execute(\n          command,\n          args: [\n                  \"--first=#{first.join(' ')}\",\n                  \"--second=#{second.join(' ')}\",\n                ],\n                 )\n\n        unless result.stdout.empty?\n          trimmed_result = result.stdout.split(\"\\n\")\n          5.times { trimmed_result.shift }\n          trimmed_result = trimmed_result.join(\"\\n\")\n\n          return [\n                   :warn,\n                   \"It appears your namespace settings between #{first} and \" \\\n                   \"#{second} are not in sync:\\n\\n#{trimmed_result}\\n\\n\" \\\n                   \"Run: chamber compare --first=#{first.join(' ')} \" \\\n                   \"--second=#{second.join(' ')}\",\n                 ]\n        end\n      end\n\n      :pass\n    end\n  end\n  # rubocop:enable Metrics/MethodLength\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/chamber_security.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `chamber secure` against any modified Chamber settings files.\n  #\n  # @see https://github.com/thekompanee/chamber\n  class ChamberSecurity < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      return :pass if result.stdout.empty?\n\n      [:fail, \"These settings appear to need to be secured but were not: #{result.stdout}\"]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/chamber_verification.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `chamber sign --verify`.\n  #\n  # @see https://github.com/thekompanee/chamber/wiki/Git-Commit-Hooks#chamber-verification-pre-commit-hook\n  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity\n  class ChamberVerification < Base\n    def run\n      approver_name  = config.fetch('approver_name') { 'your approver' }\n      approver_email = config['approver_email'] ? \" (#{config['approver_email']})\" : nil\n\n      result = execute(command)\n\n      return :pass if result.stdout.empty? && result.stderr.empty?\n      return :pass if result.stderr =~ /no signature key was found/\n\n      output = [\n        result.stdout.empty? ? nil : result.stdout,\n        result.stderr.empty? ? nil : result.stderr,\n      ].\n        compact.\n        join(\"\\n\\n\")\n\n      output = \"\\n\\n#{output}\" unless output.empty?\n\n      [\n        :warn,\n        \"One or more of your settings files does not match the signature.\\n\" \\\n        \"Talk to #{approver_name}#{approver_email} about getting them \" \\\n        \"approved.#{output}\",\n      ]\n    end\n  end\n  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/code_spell_check.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `alfonsox` spell-checking tool against any modified code file.\n  #\n  # @see https://github.com/diegojromerolopez/alfonsox\n  class CodeSpellCheck < Base\n    def run\n      # Create default file config if it does not exist\n\n      # Run spell-check\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      spellchecking_errors = result.stderr.split(\"\\n\")\n      spellchecking_errors.pop\n\n      error_messages(spellchecking_errors)\n    end\n\n    private\n\n    # Create the error messages\n    def error_messages(spellchecking_errors)\n      messages = []\n      spellchecking_errors.each do |spellchecking_error_i|\n        error_location, word = spellchecking_error_i.split(' ')\n        error_file_path, line = error_location.split(':')\n        messages << Overcommit::Hook::Message.new(\n          :error, error_file_path, line, \"#{error_location}: #{word}\"\n        )\n      end\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/coffee_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `coffeelint` against any modified CoffeeScript files.\n  #\n  # @see http://www.coffeelint.org/\n  class CoffeeLint < Base\n    MESSAGE_REGEX = /\n      ^(?<file>.+)\n      ,(?<line>\\d*),\\d*\n      ,(?<type>\\w+)\n      ,(?<msg>.+)$\n    /x.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('w') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      parse_messages(result.stdout)\n    end\n\n    private\n\n    def parse_messages(output)\n      output.scan(MESSAGE_REGEX).map do |file, line, type, msg|\n        line = line.to_i\n        type = MESSAGE_TYPE_CATEGORIZER.call(type)\n        text = \"#{file}:#{line}:#{type} #{msg}\"\n        Overcommit::Hook::Message.new(type, file, line, text)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/cook_style.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `cookstyle` against any modified Chef Ruby files.\n  #\n  # @see https://docs.chef.io/cookstyle.html\n  class CookStyle < Base\n    GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.match?(/^warn/) ? :warning : :error\n    end\n\n    COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      generic_messages = extract_messages(\n        result.stderr.split(\"\\n\"),\n        /^(?<type>[a-z]+)/i,\n        GENERIC_MESSAGE_TYPE_CATEGORIZER,\n      )\n\n      cop_messages = extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):[^ ]+ (?<type>[^ ]+)/,\n        COP_MESSAGE_TYPE_CATEGORIZER,\n      )\n\n      generic_messages + cop_messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/credo.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `credo` against any modified ex files.\n  #\n  # @see https://github.com/rrrene/credo\n  class Credo < Base\n    # example message:\n    # lib/file1.ex:1:11: R: Modules should have a @moduledoc tag.\n    # lib/file2.ex:12:81: R: Line is too long (max is 80, was 81).\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      result.stdout.split(\"\\n\").map(&:strip).reject(&:empty?).\n        map { |error| message(error) }\n    end\n\n    private\n\n    def message(error)\n      file, line = error.split(':')\n      Overcommit::Hook::Message.new(:error, file, Integer(line), error)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/css_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `csslint` against any modified CSS files.\n  #\n  # @see https://github.com/CSSLint/csslint\n  class CssLint < Base\n    MESSAGE_REGEX = /\n      ^(?<file>(?:\\w:)?[^:]+):\\s\n      (?:line\\s(?<line>\\d+)[^EW]+)?\n      (?<type>Error|Warning)\n    /x.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      return :pass if result.success? && output.empty?\n\n      extract_messages(\n        output.split(\"\\n\").reject(&:empty?),\n        MESSAGE_REGEX,\n        lambda { |type| type.downcase.to_sym }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/dart_analyzer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `dartanalyzer` against modified Dart files.\n  # @see https://dart.dev/tools/dartanalyzer\n  class DartAnalyzer < Base\n    MESSAGE_REGEX = /(?<type>.*)•\\ (?<message>[^•]+)•\\ (?<file>[^:]+):(?<line>\\d+):(\\d+)\\.*/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        lambda do |type|\n          type.include?('error') ? :error : :warning\n        end\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/dogma.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `dogma` against any modified ex files.\n  #\n  # @see https://github.com/lpil/dogma\n  class Dogma < Base\n    def run\n      result = execute command\n      return :pass if result.success?\n\n      messages = []\n      # example message:\n      #  == web/channels/user_socket.ex ==\n      #  26: LineLength: Line length should not exceed 80 chars (was 83).\n      #  1: ModuleDoc: Module Sample.UserSocket is missing a @moduledoc.\n      output = result.stdout.chomp.match(/(==.+)/m)\n\n      if output\n        output.captures.first.split(/\\n\\n/).each do |error_group|\n          errors = error_group.split /\\n/\n          file = errors.shift.gsub /[ =]/, ''\n          errors.each do |error|\n            line = error.split(': ').first\n            messages << Overcommit::Hook::Message.new(:error, file, line, \"#{file}: #{error}\")\n          end\n        end\n      end\n\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/erb_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `erblint` against any modified ERB files.\n  #\n  # @see https://github.com/Shopify/erb-lint\n  class ErbLint < Base\n    MESSAGE_REGEX = /(?<message>.+)\\nIn file: (?<file>.+):(?<line>\\d+)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\\n\")[1..],\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/es_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `eslint` against any modified JavaScript files.\n  #\n  # Protip: if you have an npm script set up to run eslint, you can configure\n  # this hook to run eslint via your npm script by using the `command` option in\n  # your .overcommit.yml file. This can be useful if you have some eslint\n  # configuration built into your npm script that you don't want to repeat\n  # somewhere else. Example:\n  #\n  #   EsLint:\n  #     required_executable: 'npm'\n  #     enabled: true\n  #     command: ['npm', 'run', 'lint', '--', '-f', 'compact']\n  #\n  # Note: This hook supports only compact format.\n  #\n  # @see http://eslint.org/\n  class EsLint < Base\n    def run\n      eslint_regex = /^(?<file>[^\\s](?:\\w:)?[^:]+):[^\\d]+(?<line>\\d+).*?(?<type>Error|Warning)/\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      messages = output.split(\"\\n\").grep(eslint_regex)\n\n      return [:fail, result.stderr] if messages.empty? && !result.success?\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.js: line 1, col 0, Error - Error message (ruleName)\n      extract_messages(messages, eslint_regex, lambda { |type| type.downcase.to_sym })\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/execute_permissions.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for files with execute permissions, which are usually not necessary\n  # in source code files (and are typically caused by a misconfigured editor\n  # assigning incorrect default permissions).\n  #\n  # Protip: if you have some files that you want to allow execute permissions\n  # on, you can disable this hook for those files by using the `exclude` option\n  # on your .overcommit.yml file. Example:\n  #\n  #   ExecutePermissions:\n  #     enabled: true\n  #     exclude:\n  #       - 'path/to/my/file/that/should/have/execute/permissions.sh'\n  #       - 'directory/that/should/have/execute/permissions/**/*'\n  class ExecutePermissions < Base\n    def run\n      file_modes = {}\n\n      # We have to look in two places to determine the execute permissions of a\n      # file. The first is the Git tree for currently known file modes of all\n      # files, the second is the index for any staged changes to file modes.\n      # Staged changes take priority if they exist.\n      #\n      # This complexity is necessary because this hook can be run in the RunAll\n      # context, where there may be no staged changes but we stil want to check\n      # the permissions.\n      extract_from_git_tree(file_modes) unless initial_commit?\n      extract_from_git_index(file_modes)\n\n      file_modes.map do |file, mode|\n        next unless execute_permissions?(mode)\n\n        Overcommit::Hook::Message.new(\n          :error,\n          file,\n          nil,\n          \"File #{file} has unnecessary execute permissions\",\n        )\n      end.compact\n    end\n\n    private\n\n    def extract_from_git_tree(file_modes)\n      result = execute(%w[git ls-tree HEAD --], args: applicable_files)\n      raise 'Unable to access git tree' unless result.success?\n\n      result.stdout.split(\"\\n\").each do |line|\n        mode, _type, _hash, file = line.split(/\\s+/, 4)\n        file_modes[file] = mode\n      end\n    end\n\n    def extract_from_git_index(file_modes)\n      result = execute(%w[git diff --raw --cached --no-color --], args: applicable_files)\n      raise 'Unable to access git index' unless result.success?\n\n      result.stdout.split(\"\\n\").each do |line|\n        _old_mode, new_mode, _old_hash, _new_hash, _status, file = line.split(/\\s+/, 6)\n        file_modes[file] = new_mode\n      end\n    end\n\n    # Check if the 1st bit is toggled, indicating execute permissions.\n    #\n    # Git tracks only execute permissions, not individual read/write/execute\n    # permissions for user, group, and other, since that concept does not exist\n    # on all operating systems. If any of the user/group/other permissions\n    # have the executable bit set, they all will. Thus we check the first bit.\n    def execute_permissions?(mode)\n      (mode.to_i(8) & 1) == 1\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/fasterer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `fasterer` against any modified Ruby files.\n  #\n  # @see https://github.com/DamirSvrtan/fasterer\n  class Fasterer < Base\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout\n\n      if extract_offense_num(output) == 0\n        :pass\n      else\n        [:warn, output]\n      end\n    end\n\n    private\n\n    def extract_offense_num(raw_output)\n      raw_output.scan(/(\\d+) offense detected/).flatten.map(&:to_i).inject(0, :+)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/file_size.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for oversized files before committing.\n  class FileSize < Base\n    def run\n      return :pass if oversized_files.empty?\n\n      oversized_files.map do |file|\n        error_message_for(file)\n      end\n    end\n\n    def description\n      \"Check for files over #{size_limit_bytes} bytes\"\n    end\n\n    private\n\n    def oversized_files\n      @oversized_files ||= build_oversized_file_list\n    end\n\n    def build_oversized_file_list\n      applicable_files.select do |file|\n        File.exist?(file) && file_size(file) > size_limit_bytes\n      end\n    end\n\n    def size_limit_bytes\n      config.fetch('size_limit_bytes')\n    end\n\n    def error_message_for(file)\n      Overcommit::Hook::Message.new(\n        :error,\n        file,\n        nil,\n        \"#{file} is #{file_size(file)} bytes\"\n      )\n    end\n\n    def file_size(file)\n      File.size(file)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/fix_me.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check for \"token\" strings\n  class FixMe < Base\n    def run\n      keywords = config['keywords']\n      result = execute(command, args: [keywords.join('|')] + applicable_files)\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/,\n        lambda { |_type| :warning }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/flay.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `flay` against any modified files.\n  #\n  # @see https://github.com/seattlerb/flay\n  class Flay < Base\n    # Flay prints two kinds of messages:\n    #\n    # 1) IDENTICAL code found in :defn (mass*2 = MASS)\n    # file_path_1.rb:LINE_1\n    # file_path_2.rb:LINE_2\n    #\n    # 2) Similar code found in :defn (mass = MASS)\n    # file_path_1.rb:LINE_1\n    # file_path_2.rb:LINE_2\n    #\n\n    def run\n      command = ['flay', '--mass', @config['mass_threshold'].to_s, '--fuzzy', @config['fuzzy'].to_s]\n      # Use a more liberal detection method\n      command += ['--liberal'] if @config['liberal']\n      messages = []\n      # Run the command for each file\n      applicable_files.each do |file|\n        result = execute(command, args: [file])\n        results = result.stdout.split(\"\\n\\n\")\n        results.shift\n        unless results.empty?\n          error_message = results.join(\"\\n\").gsub(/^\\d+\\)\\s*/, '')\n          message = Overcommit::Hook::Message.new(:error, nil, nil, error_message)\n          messages << message\n        end\n      end\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/foodcritic.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `foodcritic` against any modified Ruby files from Chef directory structure.\n  #\n  # @see http://www.foodcritic.io/\n  #\n  # There are two \"modes\" you can run this hook in based on the repo:\n  #\n  # SINGLE COOKBOOK REPO MODE\n  # -------------------------\n  # The default. Use this if your repository contains just a single cookbook,\n  # i.e. the top-level repo directory contains directories called `attributes`,\n  # `libraries`, `recipes`, etc.\n  #\n  # To get this to work well, you'll want to set your Overcommit configuration\n  # for this hook to something like:\n  #\n  # PreCommit:\n  #   Foodcritic:\n  #     enabled: true\n  #     include:\n  #       - 'attributes/**/*'\n  #       - 'definitions/**/*'\n  #       - 'files/**/*'\n  #       - 'libraries/**/*'\n  #       - 'providers/**/*'\n  #       - 'recipes/**/*'\n  #       - 'resources/**/*'\n  #       - 'templates/**/*'\n  #\n  # MONOLITHIC REPO MODE\n  # --------------------\n  # Use this if you store multiple cookbooks, environments, and roles (or any\n  # combination thereof) in a single repository.\n  #\n  # There are three configuration options relevant here:\n  #\n  #   * `cookbooks_directory`\n  #     When set, hook will treat the path as a directory containing cookbooks.\n  #     Each subdirectory of this directory will be treated as a separate\n  #     cookbook.\n  #\n  #   * `environments_directory`\n  #     When set, hook will treat the path as a directory containing environment\n  #     files.\n  #\n  #   * `roles_directory`\n  #     When set, hook will treat the given path as a directory containing role\n  #     files.\n  #\n  # In order to run in monolithic repo mode, YOU MUST SET `cookbooks_directory`.\n  # The other configuration options are optional, if you happen to store\n  # environments/roles in another repo.\n  #\n  # To get this to work well, you'll want to set your Overcommit configuration\n  # for this hook to something like:\n  #\n  # PreCommit:\n  #   Foodcritic:\n  #     enabled: true\n  #     cookbooks_directory: 'cookbooks'\n  #     environments_directory: 'environments'\n  #     roles_directory: 'roles'\n  #     include:\n  #       - 'cookbooks/**/*'\n  #       - 'environments/**/*'\n  #       - 'roles/**/*'\n  #\n  # ADDITIONAL CONFIGURATION\n  # ------------------------\n  # You can disable rules using the `flags` hook option. For example:\n  #\n  # PreCommit:\n  #   Foodcritic:\n  #     enabled: true\n  #     ...\n  #     flags:\n  #       - '--epic-fail=any'\n  #       - '-t~FC011' # Missing README in markdown format\n  #       - '-t~FC064' # Ensure issues_url is set in metadata\n  #\n  # Any other command line flag supported by the `foodcritic` executable can be\n  # specified here.\n  #\n  # If you want the hook run to fail (and not just warn), set the `on_warn`\n  # option for the hook to `fail`:\n  #\n  # PreCommit:\n  #   Foodcritic:\n  #     enabled: true\n  #     on_warn: fail\n  #     ...\n  #\n  # This will treat any warnings as failures and cause the hook to exit\n  # unsuccessfully.\n  class Foodcritic < Base\n    def run\n      args = modified_cookbooks_args + modified_environments_args + modified_roles_args\n      result = execute(command, args: args)\n\n      if result.success?\n        :pass\n      else\n        [:warn, result.stderr + result.stdout]\n      end\n    end\n\n    private\n\n    def directories_changed(dir_prefix)\n      applicable_files.\n        select    { |path| path.start_with?(dir_prefix) }.\n        map       { |path| path.gsub(%r{^#{dir_prefix}/}, '') }.\n        group_by  { |path| path.split('/').first }.\n        keys.\n        map { |path| File.join(dir_prefix, path) }\n    end\n\n    def modified_environments_args\n      modified('environments').map { |env| %W[-E #{env}] }.flatten\n    end\n\n    def modified_roles_args\n      modified('roles').map { |role| %W[-R #{role}] }.flatten\n    end\n\n    def modified_cookbooks_args\n      # Return the repo root if repository contains a single cookbook\n      if !config['cookbooks_directory'] || config['cookbooks_directory'].empty?\n        ['-B', Overcommit::Utils.repo_root]\n      else\n        # Otherwise return all modified cookbooks in the cookbook directory\n        modified('cookbooks').map { |cookbook| ['-B', cookbook] }.flatten\n      end\n    end\n\n    def modified(type)\n      return [] if !config[\"#{type}_directory\"] || config[\"#{type}_directory\"].empty?\n\n      @modified ||= {}\n      @modified[type] ||= directories_changed(full_directory_path(\"#{type}_directory\"))\n    end\n\n    def full_directory_path(config_option)\n      return config[config_option] if config[config_option].start_with?(File::SEPARATOR)\n\n      File.absolute_path(File.join(Overcommit::Utils.repo_root, config[config_option]))\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/forbidden_branches.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Prevents commits to branches matching one of the configured patterns.\n  class ForbiddenBranches < Base\n    def run\n      return :pass unless forbidden_commit?\n\n      [:fail, \"Committing to #{current_branch} is forbidden\"]\n    end\n\n    private\n\n    def forbidden_commit?\n      forbidden_branch_patterns.any? { |p| File.fnmatch(p, current_branch) }\n    end\n\n    def forbidden_branch_patterns\n      @forbidden_branch_patterns ||= Array(config['branch_patterns'])\n    end\n\n    def current_branch\n      @current_branch ||= Overcommit::GitRepo.current_branch\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/ginkgo_focus.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check for \"focused\" tests\n  class GinkgoFocus < Base\n    def run\n      keywords = config['keywords']\n      result = execute(command, args: [keywords.join('|')] + applicable_files)\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/,\n        lambda { |_type| :warning }\n      )\n    end\n\n    def applicable_test_files\n      applicable_files.select do |f|\n        f if f =~ /_test\\.go/\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/go_fmt.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs go fmt for all modified Go files\n  class GoFmt < Base\n    def run\n      errors = []\n      applicable_files.each do |file|\n        result = execute(command, args: [file])\n        errors << (result.stdout + result.stderr) unless result.success?\n      end\n      return :pass if errors.empty?\n\n      [:fail, errors.join(\"\\n\")]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/go_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `golint` against any modified Golang files.\n  #\n  # @see https://github.com/golang/lint\n  class GoLint < Base\n    def run\n      output = ''\n\n      # golint doesn't accept multiple file arguments if\n      # they belong to different packages\n      applicable_files.each do |gofile|\n        result = execute(command, args: Array(gofile))\n        output += result.stdout + result.stderr\n      end\n\n      # Unfortunately the exit code is always 0\n      return :pass if output.empty?\n\n      # example message:\n      #   path/to/file.go:1:1: Error message\n      extract_messages(\n        output.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/go_vet.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `go vet` against any modified Golang files.\n  #\n  # @see https://godoc.org/code.google.com/p/go-zh.tools/cmd/vet\n  class GoVet < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      if result.stderr.match?(/no such tool \"vet\"/)\n        return :fail, \"`go tool vet` is not installed#{install_command_prompt}\"\n      end\n\n      # example message:\n      #   path/to/file.go:7: Error message\n      extract_messages(\n        result.stderr.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/golangci_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `golangci-lint run` against any modified packages\n  #\n  # @see https://github.com/golangci/golangci-lint\n  class GolangciLint < Base\n    def run\n      packages = applicable_files.map { |f| File.dirname(f) }.uniq\n      result = execute(command, args: packages)\n      return :pass if result.success?\n      return [:fail, result.stderr] unless result.stderr.empty?\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/,\n        nil\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/hadolint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `hadolint` against any modified Dockefile files.\n  #\n  # @see http://hadolint.lukasmartinelli.ch/\n  class Hadolint < Base\n    def run\n      output = ''\n      success = true\n\n      # hadolint doesn't accept multiple arguments\n      applicable_files.each do |dockerfile|\n        result = execute(command, args: Array(dockerfile))\n        output += result.stdout\n        success &&= result.success?\n      end\n\n      return :pass if success\n\n      extract_messages(\n        output.split(\"\\n\"),\n        /^(?<file>[^:]+):(?<line>\\d+)/,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/haml_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `haml-lint` against any modified HAML files.\n  #\n  # @see https://github.com/brigade/haml-lint/\n  class HamlLint < Base\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)[^ ]* (?<type>[^ ]+)/,\n        MESSAGE_TYPE_CATEGORIZER,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/hard_tabs.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for hard tabs in files.\n  class HardTabs < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/hlint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `hlint` against any modified Haskell files.\n  #\n  # @see https://github.com/ndmitchell/hlint\n  class Hlint < Base\n    MESSAGE_REGEX = /\n      ^(?<file>(?:\\w:)?[^:]+)\n      :(?<line>\\d+)\n      :\\d+\n      :\\s*(?<type>\\w+)\n    /x.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      raw_messages = result.stdout.split(\"\\n\").grep(MESSAGE_REGEX)\n\n      # example message:\n      #   path/to/file.hs:1:0: Error: message\n      extract_messages(\n        raw_messages,\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/html_hint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `htmlhint` against any modified HTML files.\n  #\n  # @see http://htmlhint.com/\n  class HtmlHint < Base\n    def run\n      result = execute(command + applicable_files)\n      output = Overcommit::Utils.strip_color_codes(result.stdout.chomp)\n\n      message_groups = output.split(\"\\n\\n\")[0..-2]\n      message_groups.map do |group|\n        lines = group.split(\"\\n\").map(&:strip)\n        file = lines[0][/(.+):/, 1]\n        extract_messages(\n          lines[1..].map { |msg| \"#{file}: #{msg}\" },\n          /^(?<file>(?:\\w:)?[^:]+): line (?<line>\\d+)/\n        )\n      end.flatten\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/html_tidy.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `tidy` against any modified HTML files.\n  #\n  # @see http://www.html-tidy.org/\n  class HtmlTidy < Base\n    MESSAGE_REGEX = /\n      ^(?<file>(?:\\w:)?[^:]+):\\s\n      line\\s(?<line>\\d+)\\s\n      column\\s(?<col>\\d+)\\s-\\s\n      (?<type>Error|Warning):\\s(?<message>.+)$\n    /x.freeze\n\n    def run\n      # example message:\n      #   line 4 column 24 - Warning: <html> proprietary attribute \"class\"\n      applicable_files.collect do |file|\n        result = execute(command + [file])\n        output = result.stderr.chomp\n\n        extract_messages(\n          output.split(\"\\n\").collect { |msg| \"#{file}: #{msg}\" },\n          MESSAGE_REGEX,\n          lambda { |type| type.downcase.to_sym }\n        )\n      end.flatten\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/image_optim.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for images that can be optimized with `image_optim`.\n  #\n  # @see https://github.com/toy/image_optim\n  class ImageOptim < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return [:fail, result.stdout + result.stderr] unless result.success?\n\n      optimized_files = extract_optimized_files(result.stdout)\n      return :pass if optimized_files.empty?\n\n      output = \"The following images are optimizable:\\n#{optimized_files.join(\"\\n\")}\"\n      output += \"\\n\\nOptimize them by running `#{command.join(' ')} #{optimized_files.join(' ')}`\"\n      [:fail, output]\n    end\n\n    private\n\n    def extract_optimized_files(output)\n      output.split(\"\\n\").\n             select { |line| line =~ /^\\d+/ }.\n             map    { |line| line.split(/\\s+/).last }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/java_checkstyle.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `checkstyle` against any modified Java files.\n  #\n  # @see http://checkstyle.sourceforge.net/\n  class JavaCheckstyle < Base\n    MESSAGE_REGEX = /^(\\[(?<type>[^\\]]+)\\]\\s+)?(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      %w[WARN INFO].include?(type.to_s) ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      # example message:\n      #   path/to/file.java:3:5: Error message\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/js_hint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `jshint` against any modified JavaScript files.\n  #\n  # @see http://jshint.com/\n  class JsHint < Base\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.js: line 1, col 0, Error message (E001)\n      extract_messages(\n        output.split(\"\\n\").grep(/E|W/),\n        /^(?<file>(?:\\w:)?[^:]+):[^\\d]+(?<line>\\d+).+\\((?<type>E|W)\\d+\\)/,\n        lambda { |type| type.include?('W') ? :warning : :error }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/js_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `jslint` against any modified JavaScript files.\n  #\n  # @see http://www.jslint.com/\n  class JsLint < Base\n    MESSAGE_REGEX = /(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      # example message:\n      #   path/to/file.js:1:1: Error message\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/jscs.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `jscs` (JavaScript Code Style Checker) against any modified JavaScript\n  # files.\n  #\n  # @see http://jscs.info/\n  class Jscs < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      # Exit status 2 = Code style errors; everything else we don't know how to\n      # parse. https://github.com/jscs-dev/node-jscs/wiki/Exit-codes\n      unless result.status == 2\n        return :fail, result.stdout + result.stderr.chomp\n      end\n\n      # example message:\n      #   path/to/file.js: line 7, col 0, ruleName: Error message\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):[^\\d]+(?<line>\\d+)/,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/jsl.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `jsl` against any modified JavaScript files.\n  #\n  # @see http://www.javascriptlint.com/\n  class Jsl < Base\n    MESSAGE_REGEX = /(?<file>(?:\\w:)?.+)\\((?<line>\\d+)\\):(?<type>[^:]+)/.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.match?(/warning/) ? :warning : :error\n    end\n\n    def run\n      file_flags = applicable_files.map { |file| ['-process', file] }\n      result = execute(command + file_flags.flatten)\n      return :pass if result.success?\n\n      # example message:\n      #   path/to/file.js(1): lint warning: Error message\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/json_syntax.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks the syntax of any modified JSON files.\n  class JsonSyntax < Base\n    def run\n      messages = []\n\n      applicable_files.each do |file|\n        JSON.parse(IO.read(file))\n      rescue JSON::ParserError => e\n        error = \"#{e.message} parsing #{file}\"\n        messages << Overcommit::Hook::Message.new(:error, file, nil, error)\n      end\n\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/kt_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `ktlint` against modified Kotlin files.\n  # @see https://github.com/shyiko/ktlint\n  class KtLint < Base\n    MESSAGE_REGEX = /((?<file>[^:]+):(?<line>\\d+):(\\d+):(?<message>.+))/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/license_finder.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs LicenseFinder if any of your package manager declaration files have changed\n  # See more about LicenseFinder at https://github.com/pivotal/LicenseFinder\n  class LicenseFinder < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/license_header.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for license headers in source files\n  class LicenseHeader < Base\n    def run\n      begin\n        license_contents = license_lines\n      rescue Errno::ENOENT\n        return :fail, \"Unable to load license file #{license_file}\"\n      end\n\n      messages = applicable_files.map do |file|\n        check_file(file, license_contents)\n      end.compact\n\n      return :fail, messages.join(\"\\n\") if messages.any?\n\n      :pass\n    end\n\n    def check_file(file, license_contents)\n      File.readlines(file).each_with_index do |l, i|\n        if i >= license_contents.length\n          break\n        end\n\n        l.chomp!\n        unless l.end_with?(license_contents[i])\n          message = \"#{file} missing header contents from line #{i} of \"\\\n                    \"#{license_file}: #{license_contents[i]}\"\n          return message\n        end\n      end\n    end\n\n    def license_file\n      config['license_file']\n    end\n\n    def license_lines\n      @license_lines ||= begin\n        file_root = Overcommit::Utils.convert_glob_to_absolute(license_file)\n        File.read(file_root).split(\"\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/line_endings.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for line endings in files.\n  #\n  # WARNING: Works with Git 2.10.0 or newer.\n  class LineEndings < Base\n    def run\n      messages = []\n\n      offending_files.map do |file_name|\n        file = File.open(file_name)\n        begin\n          messages += check_file(file, file_name)\n        rescue ArgumentError => e\n          # File is likely a binary file which this check should ignore, but\n          # print a warning just in case\n          messages << Overcommit::Hook::Message.new(\n            :warning,\n            file_name,\n            file.lineno,\n            \"#{file_name}:#{file.lineno}:#{e.message}\"\n          )\n        end\n      end\n\n      messages\n    end\n\n    private\n\n    def check_file(file, file_name)\n      messages_for_file = []\n\n      file.each_line do |line|\n        # Remove configured line-ending\n        line.gsub!(/#{config['eol']}/, '')\n\n        # Detect any left over line-ending characters\n        next unless line.end_with?(\"\\n\", \"\\r\")\n\n        messages_for_file << Overcommit::Hook::Message.new(\n          :error,\n          file_name,\n          file.lineno,\n          \"#{file_name}:#{file.lineno}:#{line.inspect}\"\n        )\n      end\n\n      messages_for_file\n    end\n\n    def offending_files\n      result = execute(%w[git ls-files --eol -z --], args: applicable_files)\n      raise 'Unable to access git tree' unless result.success?\n\n      result.stdout.split(\"\\0\").map do |file_info|\n        info, path = file_info.split(\"\\t\")\n        i = info.split.first\n        next if i == 'l/-text' # ignore binary files\n        next if i == \"l/#{eol}\"\n\n        path\n      end.compact\n    end\n\n    def eol\n      @eol ||=  case config['eol']\n                when \"\\n\"\n                  'lf'\n                when \"\\r\\n\"\n                  'crlf'\n                else\n                  raise 'Invalid `eol` option specified: must be \"\\n\" or \"\\r\\n\"'\n                end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for local paths in files and issues a warning\n  class LocalPathsInGemfile < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      unless result.stdout.empty?\n        return :warn, \"Avoid pointing to local paths in Gemfiles:\\n#{result.stdout}\"\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/mdl.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `mdl` against any modified Markdown files\n  #\n  # @see https://github.com/mivok/markdownlint\n  class Mdl < Base\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      return :pass if result.success?\n      return [:fail, result.stderr] unless result.stderr.empty?\n\n      # example message:\n      #   [{\"filename\":\"file1.md\",\"line\":1,\"rule\":\"MD013\",\"aliases\":[\"line-length\"],\n      #   \"description\":\"Line length\"}]\n      json_messages = JSON.parse(output)\n      json_messages.map do |message|\n        Overcommit::Hook::Message.new(\n          :error,\n          message['filename'],\n          message['line'],\n          \"#{message['filename']}:#{message['line']} #{message['rule']} #{message['description']}\"\n        )\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/merge_conflicts.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for unresolved merge conflicts\n  class MergeConflicts < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      unless result.stdout.empty?\n        return :fail, \"Merge conflict markers detected:\\n#{result.stdout}\"\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/mix_format.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `mix format --check-formatted` against any modified ex/heex/exs files.\n  #\n  # @see https://hexdocs.pm/mix/main/Mix.Tasks.Format.html\n  class MixFormat < Base\n    # example message:\n    # ** (Mix) mix format failed due to --check-formatted.\n    # The following files are not formatted:\n    #\n    #   * lib/file1.ex\n    #   * lib/file2.ex\n    FILES_REGEX = /^\\s+\\*\\s+(?<file>.+)$/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      result.stderr.scan(FILES_REGEX).flatten.\n        map { |file| message(file) }\n    end\n\n    private\n\n    def message(file)\n      Overcommit::Hook::Message.new(:error, file, nil, file)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/nginx_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `nginx -t` against any modified Nginx config files.\n  #\n  # @see https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/\n  class NginxTest < Base\n    MESSAGE_REGEX = /^nginx: .+ in (?<file>.+):(?<line>\\d+)$/.freeze\n\n    def run\n      messages = []\n\n      applicable_files.each do |file|\n        result = execute(command + ['-c', file])\n        next if result.success?\n\n        messages += extract_messages(\n          result.stderr.split(\"\\n\").grep(MESSAGE_REGEX),\n          MESSAGE_REGEX\n        )\n      end\n\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pep257.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pep257` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/pep257\n  class Pep257 < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = result.stderr.chomp\n\n      # example message:\n      #   path/to/file.py:1 in public method `foo`:\n      #           D102: Docstring missing\n      extract_messages(\n        output.gsub(/:\\s+/, ': ').split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pep8.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pep8` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/pep8\n  class Pep8 < Base\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.py:88:5: E301 expected 1 blank line, found 0\n      extract_messages(\n        output.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):\\d+:\\s(?<type>E|W)/,\n        lambda { |type| type.include?('W') ? :warning : :error }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/php_cs.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `phpcs` against any modified PHP files.\n  class PhpCs < Base\n    # Parse `phpcs` csv mode output\n    MESSAGE_REGEX = /^\\\"(?<file>.+)\\\",(?<line>\\d+),\\d+,(?<type>.+),\\\"(?<msg>.+)\\\"/.freeze\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      'error'.include?(type) ? :error : :warning\n    end\n\n    def run\n      messages = []\n\n      result = execute(command, args: applicable_files)\n      if result.status\n        messages = result.stdout.split(\"\\n\")\n        # Discard the csv header\n        messages.shift\n      end\n\n      return :fail if messages.empty? && !result.success?\n      return :pass if messages.empty?\n\n      parse_messages(messages)\n    end\n\n    # Transform the CSV output into a tidy human readable message\n    def parse_messages(messages)\n      output = []\n\n      messages.map do |message|\n        message.scan(MESSAGE_REGEX).map do |file, line, type, msg|\n          type = MESSAGE_TYPE_CATEGORIZER.call(type)\n          text = \" #{file}:#{line}\\n  #{msg}\"\n          output << Overcommit::Hook::Message.new(type, file, line.to_i, text)\n        end\n      end\n\n      output\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/php_cs_fixer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `php-cs-fixer` against any modified PHP files.\n  class PhpCsFixer < Base\n    MESSAGE_REGEX = /\\s+\\d+\\)\\s+(?<file>.*\\.php)(?<violated_rules>\\s+\\(\\w+(?:,\\s+)?\\))?/.freeze\n\n    def run\n      messages = []\n      feedback = ''\n\n      # Exit status for all of the runs. Should be zero!\n      exit_status_sum = 0\n\n      applicable_files.each do |file|\n        result = execute(command, args: [file])\n        output = result.stdout.chomp\n        exit_status_sum += result.status\n\n        if result.status\n          messages = output.lstrip.split(\"\\n\")\n        end\n      end\n\n      unless messages.empty?\n        feedback = parse_messages(messages)\n      end\n\n      :pass if exit_status_sum == 0\n      :pass if feedback.empty?\n\n      feedback\n    end\n\n    def parse_messages(messages)\n      output = []\n\n      messages.map do |message|\n        message.scan(MESSAGE_REGEX).map do |file, violated_rules|\n          type = :error\n          unless violated_rules.nil?\n            type = :warning\n          end\n          text = if type == :error\n                   \"Cannot process #{file}: Syntax error\"\n                 else\n                   \"#{file} has been fixed\"\n                 end\n\n          output << Overcommit::Hook::Message.new(type, file, 0, text)\n        end\n      end\n\n      output\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/php_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `php -l` against any modified PHP files.\n  class PhpLint < Base\n    # Sample String\n    # rubocop:disable Layout/LineLength\n    #   PHP Parse error:  syntax error, unexpected 'require_once' (T_REQUIRE_ONCE) in site/sumo.php on line 12\n    # rubocop:enable Layout/LineLength\n    MESSAGE_REGEX = /^(?<type>.+)\\:\\s+(?<message>.+) in (?<file>.+) on line (?<line>\\d+)/.freeze\n\n    def run\n      # A list of error messages\n      messages = []\n\n      # Exit status for all of the runs. Should be zero!\n      exit_status_sum = 0\n\n      # Run for each of our applicable files\n      applicable_files.each do |file|\n        result = execute(command, args: [file])\n        output = result.stdout.chomp\n        exit_status_sum += result.status\n        if result.status\n          # `php -l` returns with a leading newline, and we only need the first\n          # line, there is usually some redundancy\n          messages << output.lstrip.split(\"\\n\").first\n        end\n      end\n\n      # If the sum of all lint status is zero, then none had exit status\n      return :pass if exit_status_sum == 0\n\n      # No messages is great news for us\n      return :pass if messages.empty?\n\n      # Return the list of message objects\n      extract_messages(\n        messages,\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/php_stan.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `phpstan` against any modified PHP files.\n  # For running `phpstan` with Laravel, it requires setup with `ide_helper`.\n  #\n  # References:\n  # https://github.com/phpstan/phpstan/issues/239\n  # https://gist.github.com/edmondscommerce/89695c9cd2584fefdf540fb1c528d2c2\n  class PhpStan < Base\n    MESSAGE_REGEX = /^(?<file>.+)\\:(?<line>\\d+)\\:(?<message>.+)/.freeze\n\n    def run\n      messages = []\n\n      result = execute(command, args: applicable_files)\n\n      unless result.success?\n        messages += result.stdout.lstrip.split(\"\\n\")\n      end\n\n      return :pass if messages.empty?\n\n      extract_messages(\n        messages,\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pronto.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/pronto'\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pronto`\n  #\n  # @see https://github.com/mmozuras/pronto\n  class Pronto < Base\n    include Overcommit::Hook::Shared::Pronto\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/puppet_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs 'puppet-lint' against any modified Puppet files.\n  #\n  # @see http://puppet-lint.com/\n  class PuppetLint < Base\n    MESSAGE_REGEX = /(?<file>(?:\\w:)?.+):(?<line>\\d+):\\d+:(?<type>\\w+)/.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type == 'ERROR' ? :error : :warning\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp.gsub(/^\"|\"$/, '')\n      return :pass if result.success? && output.empty?\n\n      extract_messages(\n        output.split(\"\\n\"),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  #\n  # Run's the Puppet metadata linter. It has support for adding options\n  # in the .overcommit.yaml\n  #\n  # @see https://voxpupuli.org/blog/2014/11/06/linting-metadata-json/\n  #\n  class PuppetMetadataJsonLint < Base\n    MESSAGE_REGEX = /\\((?<type>.*)\\).*/.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type == 'WARN' ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp.gsub(/^\"|\"$/, '')\n      return :pass if result.success? && output.empty?\n\n      extract_messages(\n        output.split(\"\\n\"),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pycodestyle.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pycodestyle` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/pycodestyle\n  class Pycodestyle < Base\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.py:88:5: E301 expected 1 blank line, found 0\n      extract_messages(\n        output.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):\\d+:\\s(?<type>E|W)/,\n        lambda { |type| type.include?('W') ? :warning : :error }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pydocstyle.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pydocstyle` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/pydocstyle\n  class Pydocstyle < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = result.stderr.chomp\n\n      # example message:\n      #   path/to/file.py:1 in public method `foo`:\n      #           D102: Docstring missing\n      extract_messages(\n        output.gsub(/:\\s+/, ': ').split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pyflakes.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pyflakes` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/pyflakes\n  class Pyflakes < Base\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      errors = get_messages(result.stderr, :error)\n      warnings = get_messages(result.stdout, :warning)\n\n      errors + warnings\n    end\n\n    private\n\n    def get_messages(output, type)\n      # example message:\n      #   path/to/file.py:57: local variable 'x' is assigned to but never used\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        proc { type }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/pylint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `pylint` against any modified Python files.\n  #\n  # @see http://www.pylint.org/\n  class Pylint < Base\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?.+):(?<line>\\d+):(?<type>[CEFRW])/.freeze\n\n    # Classify 'E' and 'F' message codes as errors,\n    # everything else as warnings.\n    #   http://pylint.readthedocs.org/en/latest/tutorial.html#getting-started\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      'EF'.include?(type) ? :error : :warning\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = result.stdout.chomp\n\n      # example message:\n      #   path/to/file.py:64:C: Missing function docstring (missing-docstring)\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/python_flake8.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `flake8` against any modified Python files.\n  #\n  # @see https://pypi.python.org/pypi/flake8\n  class PythonFlake8 < Base\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?.+):(?<line>\\d+):\\d+:\\s(?<type>\\w\\d+)/.freeze\n\n    # Classify 'Exxx' and 'Fxxx' message codes as errors,\n    # everything else as warnings.\n    #   http://flake8.readthedocs.org/en/latest/warnings.html\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      'EF'.include?(type[0]) ? :error : :warning\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = result.stdout.chomp\n\n      # example message:\n      #   path/to/file.py:2:13: F812 list comprehension redefines name from line 1\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/r_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/r_spec'\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `rspec` test suite\n  #\n  # @see http://rspec.info/\n  class RSpec < Base\n    include Overcommit::Hook::Shared::RSpec\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/rails_best_practices.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  module Hook\n    module PreCommit\n      # Runs `rails_best_practices` against Ruby files\n      #\n      # @see https://github.com/railsbp/rails_best_practices\n      class RailsBestPractices < Base\n        ERROR_REGEXP = /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)\\s-\\s(?<type>.+)/.freeze\n\n        def run\n          result = execute(command, args: applicable_files)\n\n          return :pass if result.success?\n          return [:fail, result.stderr] unless result.stderr.empty?\n\n          extract_messages(\n            filter_output(result.stdout),\n            ERROR_REGEXP\n          )\n        end\n\n        private\n\n        def filter_output(stdout)\n          stdout.split(\"\\n\").select do |message|\n            message.match ERROR_REGEXP\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check to see whether the schema file is in line with the migrations. When a\n  # schema file is present but a migration file is not, this is usually a\n  # failure. The exception is if the schema is at version 0 (i.e before any\n  # migrations have been run). In this case it is OK if there are no migrations.\n  class RailsSchemaUpToDate < Base\n    def run # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity\n      if migration_files.any? && schema_files.none?\n        return :fail, \"It looks like you're adding a migration, but did not update the schema file\"\n      elsif migration_files.none? && schema_files.any? && non_zero_schema_version?\n        return :fail, \"You're trying to change the schema without adding a migration file\"\n      elsif migration_files.any? && schema_files.any?\n        # Get the latest version from the migration filename. Use\n        # `File.basename` to prevent finding numbers that could appear in\n        # directories, such as the home directory of a user with a number in\n        # their username.\n        latest_version = migration_files.map do |file|\n          File.basename(file)[/\\d+/]\n        end.max\n\n        up_to_date = schema.include?(latest_version)\n\n        unless up_to_date\n          return :fail, \"The latest migration version you're committing is \" \\\n                       \"#{latest_version}, but your schema file \" \\\n                       \"#{schema_files.join(' or ')} is on a different version.\"\n        end\n      end\n\n      :pass\n    end\n\n    private\n\n    def encoding\n      return unless @config.key?('encoding')\n\n      { encoding: @config['encoding'] }.compact\n    end\n\n    def migration_files\n      @migration_files ||= applicable_files.select do |file|\n        file.match %r{db/migrate/.*\\.rb}\n      end\n    end\n\n    def schema_files\n      @schema_files ||= applicable_files.select do |file|\n        file.match %r{db/schema\\.rb|db/structure.*\\.sql}\n      end\n    end\n\n    def schema\n      @schema ||= schema_files.map { |file| File.read(file, **(encoding || {})) }.join\n      @schema.tr('_', '')\n    end\n\n    def non_zero_schema_version?\n      schema =~ /\\d{14}/\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/rake_target.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/rake_target'\n\nmodule Overcommit::Hook::PreCommit\n  # Runs rake targets\n  #\n  # @see Overcommit::Hook::Shared::RakeTarget\n  class RakeTarget < Base\n    include Overcommit::Hook::Shared::RakeTarget\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/reek.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `reek` against any modified Ruby files.\n  #\n  # @see https://github.com/troessner/reek\n  class Reek < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = scrub_output(result.stdout + result.stderr)\n\n      extract_messages(\n        output,\n        /^\\s*(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):/,\n      )\n    end\n\n    private\n\n    def scrub_output(raw_output)\n      raw_output.split(\"\\n\").grep(/^(.(?!warning))*$/)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/rst_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `rst-lint` against any modified reStructuredText files\n  #\n  # @see https://github.com/twolfson/restructuredtext-lint\n  class RstLint < Base\n    MESSAGE_REGEX = /\n    ^(?<type>INFO|WARNING|ERROR|SEVERE)(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)\\s(?<msg>.+)\n    /x.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n\n      return :pass if result.success?\n      return [:fail, result.stderr] unless result.stderr.empty?\n\n      # example message:\n      # WARNING README.rst:7 Title underline too short.\n      extract_messages(\n        output.split(\"\\n\"),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/rubo_cop.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `rubocop` against any modified Ruby files.\n  #\n  # @see http://batsov.com/rubocop/\n  class RuboCop < Base\n    GENERIC_MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.match?(/^warn/) ? :warning : :error\n    end\n\n    COP_MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      generic_messages = extract_messages(\n        result.stderr.split(\"\\n\"),\n        /^(?<type>[a-z]+)/i,\n        GENERIC_MESSAGE_TYPE_CATEGORIZER,\n      )\n\n      cop_messages = extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):[^ ]+ (?<type>[^ ]+)/,\n        COP_MESSAGE_TYPE_CATEGORIZER,\n      )\n\n      generic_messages + cop_messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/ruby_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `ruby-lint` against any modified Ruby files.\n  #\n  # @see https://github.com/YorickPeterse/ruby-lint\n  class RubyLint < Base\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<type>[^:]+):(?<line>\\d+)/,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/ruby_syntax.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `ruby -c` against all Ruby files.\n  #\n  class RubySyntax < Base\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.match?(/^(syntax)?\\s*error/) ? :error : :warning\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n\n      result_lines = result.stderr.split(\"\\n\")\n\n      return :pass if result_lines.length.zero?\n\n      # Example message:\n      #   path/to/file.rb:1: syntax error, unexpected '^'\n      extract_messages(\n        result_lines,\n        /^(?<file>[^:]+):(?<line>\\d+):\\s*(?<type>[^,]+),\\s*(?<message>.+)/,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/scalariform.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `scalariform` against any modified Scala files.\n  #\n  # @see https://github.com/mdr/scalariform\n  class Scalariform < Base\n    MESSAGE_REGEX = /^\\[(?<type>FAILED|ERROR)\\]\\s+(?<file>(?:\\w:)?.+)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n\n      # example message:\n      #   [FAILED] path/to/file.scala\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX,\n        lambda { |type| type == 'ERROR' ? :error : :warning }\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/scalastyle.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `scalastyle` against any modified Scala files.\n  #\n  # @see http://www.scalastyle.org/\n  class Scalastyle < Base\n    MESSAGE_REGEX = /\n      ^(?<type>error|warning)\\s\n      file=(?<file>(?:\\w:)?.+)\\s\n      message=.+\\s*\n      (line=(?<line>\\d+))?\n    /x.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp + result.stderr.chomp\n      messages = output.split(\"\\n\").grep(MESSAGE_REGEX)\n\n      return [:fail, output] unless result.success? || messages.any?\n\n      # example message:\n      #   error file=/path/to/file.scala message=Error message line=1 column=1\n      extract_messages(\n        messages,\n        MESSAGE_REGEX,\n        lambda(&:to_sym)\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/scss_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `scss-lint` against any modified SCSS files.\n  #\n  # @see https://github.com/sds/scss-lint\n  class ScssLint < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      # Status code 81 indicates the applicable files were all filtered by\n      # exclusions defined by the configuration. In this case, we're happy to\n      # return success since there were technically no lints.\n      return :pass if [0, 81].include?(result.status)\n\n      # Any status that isn't indicating lint warnings or errors indicates failure\n      return :fail, (result.stdout + result.stderr) unless [1, 2].include?(result.status)\n\n      begin\n        collect_lint_messages(JSON.parse(result.stdout))\n      rescue JSON::ParserError => e\n        [:fail, \"Unable to parse JSON returned by SCSS-Lint: #{e.message}\\n\" \\\n                      \"STDOUT: #{result.stdout}\\nSTDERR: #{result.stderr}\"]\n      end\n    end\n\n    private\n\n    def collect_lint_messages(files_to_lints)\n      files_to_lints.flat_map do |path, lints|\n        lints.map do |lint|\n          severity = lint['severity'] == 'warning' ? :warning : :error\n\n          message = lint['reason']\n          message = \"#{lint['linter']}: #{message}\" if lint['linter']\n          message = \"#{path}:#{lint['line']} #{message}\"\n\n          Overcommit::Hook::Message.new(severity, path, lint['line'], message)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/semi_standard.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `semistandard` against any modified JavaScript files.\n  #\n  # @see https://github.com/Flet/semistandard\n  class SemiStandard < Base\n    MESSAGE_REGEX = /^\\s*(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.js:1:1: Error message (ruleName)\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX), # ignore header line\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/shell_check.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `shellcheck` against any modified shell script files.\n  #\n  # @see http://www.shellcheck.net/\n  class ShellCheck < Base\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('note') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):[^ ]+ (?<type>[^ ]+)/,\n        MESSAGE_TYPE_CATEGORIZER,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/slim_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `slim-lint` against any modified Slim templates.\n  #\n  # @see https://github.com/sds/slim-lint\n  class SlimLint < Base\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('W') ? :warning : :error\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)[^ ]* (?<type>[^ ]+)/,\n        MESSAGE_TYPE_CATEGORIZER,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/solargraph.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit'\nrequire 'overcommit/hook/pre_commit/base'\n\nmodule Overcommit\n  module Hook\n    module PreCommit\n      # Runs `solargraph typecheck` against any modified Ruby files.\n      #\n      # @see https://github.com/castwide/solargraph\n      class Solargraph < Base\n        MESSAGE_REGEX = /^\\s*(?<file>(?:\\w:)?[^:]+):(?<line>\\d+) - /.freeze\n\n        def run\n          result = execute(command, args: applicable_files)\n          return :pass if result.success?\n\n          stderr_lines = remove_harmless_glitches(result.stderr)\n          violation_lines = result.stdout.split(\"\\n\").grep(MESSAGE_REGEX)\n          if violation_lines.empty?\n            if stderr_lines.empty?\n              [:fail, 'Solargraph failed to run']\n            else\n              # let's feed it stderr so users see the errors\n              extract_messages(stderr_lines, MESSAGE_REGEX)\n            end\n          else\n            extract_messages(violation_lines, MESSAGE_REGEX)\n          end\n        end\n\n        private\n\n        # @param stderr [String]\n        #\n        # @return [Array<String>]\n        def remove_harmless_glitches(stderr)\n          stderr.split(\"\\n\").reject do |line|\n            line.include?('[WARN]') ||\n              line.include?('warning: parser/current is loading') ||\n              line.include?('Please see https://github.com/whitequark')\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/sorbet.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs 'srb tc' against any modified files.\n  #\n  # @see https://github.com/sorbet/sorbet\n  class Sorbet < Base\n    # example of output:\n    # sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003\n    MESSAGE_REGEX = /^(?<file>[^:]+):(?<line>\\d+): (?<message>.*)$/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      output = result.stderr.split(\"\\n\").grep(MESSAGE_REGEX)\n\n      extract_messages(\n        output,\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/sqlint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs 'sqlint' against any modified SQL files.\n  #\n  # @see https://github.com/purcell/sqlint\n  class Sqlint < Base\n    MESSAGE_REGEX = /(?<file>(?:\\w:)?.+):(?<line>\\d+):\\d+:(?<type>\\w+)/.freeze\n\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type == 'ERROR' ? :error : :warning\n    end\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      return :pass if result.success? && output.empty?\n\n      extract_messages(\n        output.split(\"\\n\"),\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/standard.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `standard` against any modified JavaScript files.\n  #\n  # @see https://github.com/feross/standard\n  class Standard < Base\n    MESSAGE_REGEX = /^\\s*(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.js:1:1: Error message (ruleName)\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX), # ignore header line\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/stylelint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `stylelint` against any modified CSS file.\n  #\n  # @see https://github.com/stylelint/stylelint\n  class Stylelint < Base\n    # example of output:\n    # index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)\n\n    MESSAGE_REGEX = /^(?<file>[^:]+):\\D*(?<line>\\d+).*$/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout + result.stderr.chomp\n      return :pass if result.success? && output.empty?\n\n      extract_messages(\n        output.split(\"\\n\"),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/swift_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `swiftlint lint` against modified Swift files.\n  # @see https://github.com/realm/SwiftLint\n  class SwiftLint < Base\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)[^ ]* (?<type>[^ ]+):(?<message>.*)/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      extract_messages(\n        result.stdout.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/terraform_format.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs 'terraform fmt' against any modified *.tf files.\n  #\n  # @see https://www.terraform.io/docs/commands/fmt.html\n  class TerraformFormat < Base\n    def run\n      messages = []\n      applicable_files.each do |f|\n        result = execute(command, args: [f])\n        unless result.success?\n          messages << Overcommit::Hook::Message.new(:error, f, nil, \"violation found in #{f}\")\n        end\n      end\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/trailing_whitespace.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks for trailing whitespace in files.\n  class TrailingWhitespace < Base\n    def run\n      result = execute(command, args: applicable_files)\n\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/,\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/travis_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `travis-lint` against any modified Travis CI files.\n  #\n  # @see https://github.com/travis-ci/travis.rb\n  class TravisLint < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      [:fail, (result.stdout + result.stderr).strip]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/ts_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `tslint` against modified TypeScript files.\n  # @see http://palantir.github.io/tslint/\n  class TsLint < Base\n    # example message:\n    # \"src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140\"\n    # or\n    # \"ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef\"\n    MESSAGE_REGEX = /^(?<type>.+: )?(?<file>.+?(?=\\[))[^\\d]+(?<line>\\d+).*?/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stdout.chomp\n      return :pass if result.success? && output.empty?\n\n      output_lines = output.split(\"\\n\").map(&:strip).reject(&:empty?)\n      type_categorizer = ->(type) { type.nil? || type.include?('ERROR') ? :error : :warning }\n\n      extract_messages(\n        output_lines,\n        MESSAGE_REGEX,\n        type_categorizer\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/vint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `vint` against any modified Vim script files.\n  #\n  # @see https://github.com/Kuniwak/vint\n  class Vint < Base\n    def run\n      result = execute(command, args: applicable_files)\n      return :pass if result.success?\n\n      return [:fail, result.stderr] unless result.stderr.empty?\n\n      # example message:\n      #   path/to/file.vim:1:1: Error message\n      extract_messages(\n        result.stdout.split(\"\\n\"),\n        /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+)/\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/w3c_css.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `w3c_validators` against any modified CSS files.\n  #\n  # @see https://github.com/alexdunae/w3c_validators\n  class W3cCss < Base\n    def run\n      collect_messages\n    rescue W3CValidators::ParsingError,\n           W3CValidators::ValidatorUnavailable => e\n      [:fail, e.message]\n    end\n\n    private\n\n    def collect_messages\n      applicable_files.collect do |path|\n        results = validator.validate_file(path)\n        messages = results.errors + results.warnings\n        messages.collect do |msg|\n          # Some warnings are not per-line, so use 0 as a default\n          line = Integer(msg.line || 0)\n\n          # Build message by hand to reduce noise from the validator response\n          text = \"#{msg.type.to_s.upcase}; URI: #{path}; line #{line}: #{msg.message.strip}\"\n          Overcommit::Hook::Message.new(msg.type, path, line, text)\n        end\n      end.flatten\n    end\n\n    def validator\n      unless @validator\n        @validator = W3CValidators::CSSValidator.new(opts)\n        @validator.set_language!(language) unless language.nil?\n        @validator.set_profile!(profile) unless profile.nil?\n        @validator.set_warn_level!(warn_level) unless warn_level.nil?\n      end\n      @validator\n    end\n\n    def opts\n      @opts ||= {\n        validator_uri: config['validator_uri'],\n        proxy_server: config['proxy_server'],\n        proxy_port: config['proxy_port'],\n        proxy_user: config['proxy_user'],\n        proxy_pass: config['proxy_pass']\n      }\n    end\n\n    def language\n      @language ||= config['language']\n    end\n\n    # Values specified at\n    #   http://www.rubydoc.info/gems/w3c_validators/1.2/W3CValidators#CSS_PROFILES\n    def profile\n      @profile ||= config['profile']\n    end\n\n    # One of 0, 1, 2, 'no'\n    def warn_level\n      @warn_level ||= config['warn_level']\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/w3c_html.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `w3c_validators` against any modified HTML files.\n  #\n  # @see https://github.com/alexdunae/w3c_validators\n  class W3cHtml < Base\n    def run\n      collect_messages\n    rescue W3CValidators::ParsingError,\n           W3CValidators::ValidatorUnavailable => e\n      [:fail, e.message]\n    end\n\n    private\n\n    def collect_messages\n      applicable_files.collect do |path|\n        results = validator.validate_file(path)\n        messages = results.errors + results.warnings\n        messages.collect do |msg|\n          # Some warnings are not per-line, so use 0 as a default\n          line = Integer(msg.line || 0)\n\n          # Build message by hand to reduce noise from the validator response\n          text = \"#{msg.type.to_s.upcase}; URI: #{path}; line #{line}: #{msg.message.strip}\"\n          Overcommit::Hook::Message.new(msg.type, path, line, text)\n        end\n      end.flatten\n    end\n\n    def validator\n      unless @validator\n        @validator = W3CValidators::MarkupValidator.new(opts)\n        @validator.set_charset!(charset, true) unless charset.nil?\n        @validator.set_doctype!(doctype, true) unless doctype.nil?\n        @validator.set_debug!\n      end\n      @validator\n    end\n\n    def opts\n      @opts ||= {\n        validator_uri: config['validator_uri'],\n        proxy_server: config['proxy_server'],\n        proxy_port: config['proxy_port'],\n        proxy_user: config['proxy_user'],\n        proxy_pass: config['proxy_pass']\n      }\n    end\n\n    # Values specified at\n    #   http://www.rubydoc.info/gems/w3c_validators/1.2/W3CValidators#CHARSETS\n    def charset\n      @charset ||= config['charset']\n    end\n\n    # Values specified at\n    #   http://www.rubydoc.info/gems/w3c_validators/1.2/W3CValidators#DOCTYPES\n    def doctype\n      @doctype ||= config['doctype']\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/xml_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `xmllint` against any modified XML files.\n  #\n  # @see http://xmlsoft.org/xmllint.html\n  class XmlLint < Base\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+):/.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      output = result.stderr.chomp\n\n      return :pass if result.success? && output.empty?\n\n      # example message:\n      #   path/to/file.xml:1: parser error : Error message\n      extract_messages(\n        output.split(\"\\n\").grep(MESSAGE_REGEX),\n        MESSAGE_REGEX\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/xml_syntax.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks the syntax of any modified XML files.\n  class XmlSyntax < Base\n    def run\n      messages = []\n\n      applicable_files.each do |file|\n        REXML::Document.new(IO.read(file))\n      rescue REXML::ParseException => e\n        error = \"Error parsing #{file}: #{e.message}\"\n        messages << Overcommit::Hook::Message.new(:error, file, nil, error)\n      end\n\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/yaml_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Runs `YAMLLint` against any modified YAML files.\n  #\n  # @see https://github.com/adrienverge/yamllint\n  class YamlLint < Base\n    MESSAGE_REGEX = /\n      ^(?<file>.+)\n      :(?<line>\\d+)\n      :(?<col>\\d+)\n      :\\s\\[(?<type>\\w+)\\]\n      \\s(?<msg>.+)$\n    /x.freeze\n\n    def run\n      result = execute(command, args: applicable_files)\n      parse_messages(result.stdout)\n    end\n\n    private\n\n    def parse_messages(output)\n      repo_root = Overcommit::Utils.repo_root\n\n      output.scan(MESSAGE_REGEX).map do |file, line, col, type, msg|\n        line = line.to_i\n        type = type.to_sym\n        # Obtain the path relative to the root of the repository\n        # for nicer output:\n        relpath = file.dup\n        relpath.slice!(\"#{repo_root}/\")\n\n        text = \"#{relpath}:#{line}:#{col}:#{type} #{msg}\"\n        Overcommit::Hook::Message.new(type, file, line, text)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/yaml_syntax.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Checks the syntax of any modified YAML files.\n  class YamlSyntax < Base\n    def run\n      messages = []\n\n      applicable_files.each do |file|\n        YAML.load_file(file, aliases: true)\n      rescue ArgumentError\n        begin\n          YAML.load_file(file)\n        rescue ArgumentError, Psych::SyntaxError => e\n          messages << Overcommit::Hook::Message.new(:error, file, nil, e.message)\n        end\n      rescue Psych::DisallowedClass => e\n        messages << error_message(file, e)\n      end\n\n      messages\n    end\n\n    private\n\n    def error_message(file, error)\n      text = \"#{file}: #{error.message}\"\n      Overcommit::Hook::Message.new(:error, file, nil, text)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/yard_coverage.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Class to check yard documentation coverage.\n  #\n  # Use option \"min_coverage_percentage\" in your YardCoverage configuration\n  # to set your desired documentation coverage percentage.\n  #\n  class YardCoverage < Base\n    def run\n      # Run a no-stats yard command to get the coverage\n      args = flags + applicable_files\n      result = execute(command, args: args)\n\n      warnings_and_stats_text, undocumented_objects_text =\n        result.stdout.split('Undocumented Objects:')\n\n      warnings_and_stats = warnings_and_stats_text.strip.split(\"\\n\")\n\n      # Stats are the last 7 lines before the undocumented objects\n      stats = warnings_and_stats.slice(-7, 7)\n\n      # If no stats present (shouldn't happen), warn the user and end\n      if stats.class != Array || stats.length != 7\n        return [:warn, 'Impossible to read the yard stats. Please, check your yard installation.']\n      end\n\n      # Check the yard coverage\n      yard_coverage = check_yard_coverage(stats)\n      if yard_coverage == :warn\n        return [\n          :warn,\n          'Impossible to read yard doc coverage. Please, check your yard installation.'\n        ]\n      end\n      return :pass if yard_coverage == :pass\n\n      error_messages(yard_coverage, undocumented_objects_text)\n    end\n\n    private\n\n    # Check the yard coverage\n    #\n    # Return a :pass if the coverage is enough, :warn if it couldn't be read,\n    # otherwise, it has been read successfully.\n    #\n    def check_yard_coverage(stat_lines)\n      if config['min_coverage_percentage']\n        match = stat_lines.last.match(/^\\s*([\\d.]+)%\\s+documented\\s*$/)\n        unless match\n          return :warn\n        end\n\n        yard_coverage = match.captures[0].to_f\n        if yard_coverage >= config['min_coverage_percentage'].to_f\n          return :pass\n        end\n\n        yard_coverage\n      end\n    end\n\n    # Create the error messages\n    def error_messages(yard_coverage, error_text)\n      first_message = \"You have a #{yard_coverage}% yard documentation coverage. \"\\\n                      \"#{config['min_coverage_percentage']}% is the minimum required.\"\n\n      # Add the undocumented objects text as error messages\n      messages = [Overcommit::Hook::Message.new(:error, nil, nil, first_message)]\n\n      errors = error_text.strip.split(\"\\n\")\n      errors.each do |undocumented_object|\n        undocumented_object_message, file_info = undocumented_object.split(/:?\\s+/)\n        file_info_match = file_info.match(/^\\(([^:]+):(\\d+)\\)/)\n\n        # In case any compacted error does not follow the format, ignore it\n        if file_info_match\n          file = file_info_match.captures[0]\n          line = file_info_match.captures[1]\n          messages << Overcommit::Hook::Message.new(\n            :error, file, line, \"#{file}:#{line}: #{undocumented_object_message}\"\n          )\n        end\n      end\n      messages\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_commit/yarn_check.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreCommit\n  # Check if local yarn.lock matches package.json when either changes, unless\n  # yarn.lock is ignored by git.\n  #\n  # @see https://yarnpkg.com/en/docs/cli/check\n  class YarnCheck < Base\n    LOCK_FILE = 'yarn.lock'\n\n    # A lot of the errors returned by `yarn check` are outside the developer's control\n    # (are caused by bad package specification, in the hands of the upstream maintainer)\n    # So limit reporting to errors the developer can do something about\n    ACTIONABLE_ERRORS = [\n      'Lockfile does not contain pattern',\n    ].freeze\n\n    def run\n      # Ignore if yarn.lock is not tracked by git\n      ignored_files = execute(%w[git ls-files -o -i --exclude-standard]).stdout.split(\"\\n\")\n      return :pass if ignored_files.include?(LOCK_FILE)\n\n      previous_lockfile = File.exist?(LOCK_FILE) ? File.read(LOCK_FILE) : nil\n      result = execute(command)\n      new_lockfile = File.exist?(LOCK_FILE) ? File.read(LOCK_FILE) : nil\n\n      # `yarn check` also throws many warnings, which should be ignored here\n      errors_regex = Regexp.new(\"^error (.*)(#{ACTIONABLE_ERRORS.join('|')})(.*)$\")\n      errors = errors_regex.match(result.stderr)\n      unless errors.nil? && previous_lockfile == new_lockfile\n        return :fail, \"#{LOCK_FILE} is not up-to-date -- run `yarn install`\"\n      end\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\nrequire 'overcommit/utils/messages_utils'\n\nmodule Overcommit::Hook::PrePush\n  # Functionality common to all pre-push hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context, :remote_name, :remote_url, :pushed_refs\n\n    def run?\n      super &&\n        !exclude_remotes.include?(remote_name) &&\n        (include_remote_ref_deletions? || !@context.remote_ref_deletion?)\n    end\n\n    private\n\n    def extract_messages(*args)\n      Overcommit::Utils::MessagesUtils.extract_messages(*args)\n    end\n\n    def exclude_remotes\n      @config['exclude_remotes'] || []\n    end\n\n    def include_remote_ref_deletions?\n      @config['include_remote_ref_deletions']\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/brakeman.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `brakeman` whenever Ruby/Rails files change.\n  #\n  # @see http://brakemanscanner.org/\n  class Brakeman < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      [:fail, result.stdout]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/cargo_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `cargo test` before push if Rust files changed\n  class CargoTest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      [:fail, result.stdout]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/flutter_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs Flutter test suite (`flutter test`) before push\n  #\n  # @see https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html\n  class FlutterTest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/go_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `go test ./...` command on prepush\n  class GoTest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/golangci_lint.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs golangci-lint\n  #\n  # @see https://github.com/golangci/golangci-lint\n  class GolangciLint < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/minitest.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `minitest` test suite before push\n  #\n  # @see https://github.com/seattlerb/minitest\n  class Minitest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n\n    def command\n      super + included_files.map { |file| \"-r#{file}\" }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/mix_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `mix test` test suite before push\n  #\n  # @see https://hexdocs.pm/mix/Mix.Tasks.Test.html\n  class MixTest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/php_unit.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `phpunit` test suite before push\n  #\n  # @see https://phpunit.de/\n  class PhpUnit < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/pronto.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/pronto'\n\nmodule Overcommit::Hook::PrePush\n  # Runs `pronto`\n  #\n  # @see https://github.com/mmozuras/pronto\n  class Pronto < Base\n    include Overcommit::Hook::Shared::Pronto\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/protected_branches.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Prevents updates to specified branches.\n  # Accepts a 'destructive_only' option globally or per branch\n  # to only prevent destructive updates.\n  class ProtectedBranches < Base\n    def run\n      return :pass unless illegal_pushes.any?\n\n      messages = illegal_pushes.map do |pushed_ref|\n        \"Deleting or force-pushing to #{pushed_ref.remote_ref} is not allowed.\"\n      end\n\n      [:fail, messages.join(\"\\n\")]\n    end\n\n    private\n\n    def illegal_pushes\n      @illegal_pushes ||= pushed_refs.select do |pushed_ref|\n        protected?(pushed_ref)\n      end\n    end\n\n    def protected?(ref)\n      find_pattern(ref.remote_ref)&.destructive?(ref)\n    end\n\n    def find_pattern(remote_ref)\n      ref_name = remote_ref[%r{refs/heads/(.*)}, 1]\n      return if ref_name.nil?\n\n      patterns.find do |pattern|\n        File.fnmatch(pattern.to_s, ref_name)\n      end\n    end\n\n    def patterns\n      @patterns ||= fetch_patterns\n    end\n\n    def fetch_patterns\n      branch_configurations.map do |pattern|\n        if pattern.is_a?(Hash)\n          Pattern.new(pattern.keys.first, pattern['destructive_only'])\n        else\n          Pattern.new(pattern, global_destructive_only?)\n        end\n      end\n    end\n\n    def branch_configurations\n      config['branches'].to_a + config['branch_patterns'].to_a\n    end\n\n    def global_destructive_only?\n      config['destructive_only'].nil? || config['destructive_only']\n    end\n\n    Pattern = Struct.new('Pattern', :name, :destructive_only) do\n      alias_method :to_s, :name\n      alias_method :destructive_only?, :destructive_only\n\n      def destructive?(ref)\n        if destructive_only?\n          ref.destructive?\n        else\n          true\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/pub_test.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs Dart test suite (`pub run test`) before push\n  #\n  # @see https://pub.dev/packages/test#running-tests\n  class PubTest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/pytest.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `pytest` test suite before push\n  #\n  # @see https://github.com/pytest-dev/pytest\n  class Pytest < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/python_nose.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `nose` test suite before push\n  #\n  # @see https://nose.readthedocs.io/en/latest/\n  class PythonNose < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/r_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/r_spec'\n\nmodule Overcommit::Hook::PrePush\n  # Runs `rspec` test suite\n  #\n  # @see http://rspec.info/\n  class RSpec < Base\n    include Overcommit::Hook::Shared::RSpec\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/rake_target.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/hook/shared/rake_target'\n\nmodule Overcommit::Hook::PrePush\n  # Runs rake targets\n  #\n  # @see Overcommit::Hook::Shared::RakeTarget\n  class RakeTarget < Base\n    include Overcommit::Hook::Shared::RakeTarget\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_push/test_unit.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrePush\n  # Runs `test-unit` test suite before push\n  #\n  # @see https://github.com/test-unit/test-unit\n  class TestUnit < Base\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_rebase/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PreRebase\n  # Functionality common to all pre-rebase hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context,\n                   :upstream_branch, :rebased_branch, :detached_head?,\n                   :fast_forward?, :rebased_commits\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/pre_rebase/merged_commits.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PreRebase\n  # Prevents rebasing commits that have already been merged into one of\n  # a specified set of branches.\n  class MergedCommits < Base\n    def run\n      # Allow rebasing a detached HEAD since no refs are changed.\n      return :pass if detached_head? || illegal_commits.empty?\n\n      message = 'Cannot rebase commits that have already been merged into ' \\\n                \"one of #{branches.join(', ')}\"\n\n      [:fail, message]\n    end\n\n    private\n\n    def branches\n      @branches ||= config['branches']\n    end\n\n    def illegal_commits\n      @illegal_commits ||= rebased_commits.select do |commit_sha1|\n        branches_containing_commit =\n          Overcommit::GitRepo.branches_containing_commit(commit_sha1)\n        (branches_containing_commit & branches).any?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/prepare_commit_msg/base.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'forwardable'\n\nmodule Overcommit::Hook::PrepareCommitMsg\n  # Functionality common to all prepare-commit-msg hooks.\n  class Base < Overcommit::Hook::Base\n    extend Forwardable\n\n    def_delegators :@context,\n                   :commit_message_filename, :commit_message_source, :commit, :lock\n\n    def modify_commit_message\n      raise 'This expects a block!' unless block_given?\n\n      # NOTE: this assumes all the hooks of the same type share the context's\n      # memory. If that's not the case, this won't work.\n      lock.synchronize do\n        contents = File.read(commit_message_filename)\n        File.open(commit_message_filename, 'w') do |f|\n          f << (yield contents)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/prepare_commit_msg/replace_branch.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::PrepareCommitMsg\n  # Prepends the commit message with a message based on the branch name.\n  #\n  # === What to prepend\n  #\n  # It's possible to reference parts of the branch name through the captures in\n  # the `branch_pattern` regex.\n  #\n  # For instance, if your current branch is `123-topic` then this config\n  #\n  #    branch_pattern: '(\\d+)-(\\w+)'\n  #    replacement_text: '[#\\1] '\n  #\n  # would make this hook prepend commit messages with `[#123] `.\n  #\n  # Similarly, a replacement text of `[\\1][\\2]` would result in `[123][topic]`.\n  #\n  # == When to run this hook\n  #\n  # You can configure this to run only for specific types of commits by setting\n  # the `skipped_commit_types`. The allowed types are\n  #\n  # - 'message'  - if message is given via `-m`, `-F`\n  # - 'template' - if `-t` is given or `commit.template` is set\n  # - 'commit'   - if `-c`, `-C`, or `--amend` is given\n  # - 'merge'    - if merging\n  # - 'squash'   - if squashing\n  #\n  class ReplaceBranch < Base\n    DEFAULT_BRANCH_PATTERN = /\\A(\\d+)-(\\w+).*\\z/.freeze\n\n    def run\n      return :pass if skip?\n\n      Overcommit::Utils.log.debug(\n        \"Checking if '#{Overcommit::GitRepo.current_branch}' matches #{branch_pattern}\"\n      )\n\n      return :warn unless branch_pattern.match?(Overcommit::GitRepo.current_branch)\n\n      Overcommit::Utils.log.debug(\"Writing #{commit_message_filename} with #{new_template}\")\n\n      modify_commit_message do |old_contents|\n        \"#{new_template}#{old_contents}\"\n      end\n\n      :pass\n    end\n\n    def new_template\n      @new_template ||=\n        begin\n          curr_branch = Overcommit::GitRepo.current_branch\n          curr_branch.gsub(branch_pattern, replacement_text)\n        end\n    end\n\n    def branch_pattern\n      @branch_pattern ||=\n        begin\n          pattern = config['branch_pattern']\n          Regexp.new((pattern || '').empty? ? DEFAULT_BRANCH_PATTERN : pattern)\n        end\n    end\n\n    def replacement_text\n      @replacement_text ||=\n        begin\n          if File.exist?(replacement_text_config)\n            File.read(replacement_text_config).chomp\n          else\n            replacement_text_config\n          end\n        end\n    end\n\n    def replacement_text_config\n      @replacement_text_config ||= config['replacement_text']\n    end\n\n    def skipped_commit_types\n      @skipped_commit_types ||= config['skipped_commit_types'].map(&:to_sym)\n    end\n\n    def skip?\n      super || skipped_commit_types.include?(commit_message_source)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/bower_install.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all BowerInstall hooks. Runs `bower install` when a\n  # change is detected in the repository's dependencies.\n  #\n  # @see http://bower.io/\n  module BowerInstall\n    def run\n      result = execute(command)\n      return :fail, result.stderr unless result.success?\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/bundle_install.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all BundleInstall hooks. Runs `bundle install` when a\n  # change is detected in the repository's dependencies.\n  #\n  # @see http://bundler.io/\n  module BundleInstall\n    def run\n      result = execute(command)\n      return :fail, result.stdout unless result.success?\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/composer_install.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all ComposerInstall hooks. Runs `composer install` when\n  # a change is detected in the repository's dependencies.\n  #\n  # @see https://getcomposer.org/\n  module ComposerInstall\n    def run\n      result = execute(command)\n      return :fail, result.stdout unless result.success?\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/index_tags.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all IndexTags hooks. It runs ctags in the background so\n  # your tag definitions are up-to-date.\n  #\n  # @see http://ctags.sourceforge.net/\n  module IndexTags\n    def run\n      execute_in_background([Overcommit::Utils.script_path('index-tags')])\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/npm_install.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all NpmInstall hooks. Runs `npm install` when a change\n  # is detected in the repository's dependencies.\n  #\n  # @see https://www.npmjs.com/\n  module NpmInstall\n    def run\n      result = execute(command)\n      return :fail, result.stderr unless result.success?\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/pronto.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all Pronto hooks. Runs pronto linters.\n\n  # @see https://github.com/prontolabs/pronto\n  module Pronto\n    MESSAGE_TYPE_CATEGORIZER = lambda do |type|\n      type.include?('E') ? :error : :warning\n    end\n\n    MESSAGE_REGEX = /^(?<file>(?:\\w:)?[^:]+):(?<line>\\d+) (?<type>[^ ]+)/.freeze\n\n    def run\n      result = execute(command)\n      return :pass if result.success?\n\n      # e.g. runtime errors\n      generic_errors = extract_messages(\n        result.stderr.split(\"\\n\"),\n        /^(?<type>[a-z]+)/i\n      )\n\n      pronto_infractions = extract_messages(\n        result.stdout.split(\"\\n\").select { |line| line.match?(MESSAGE_REGEX) },\n        MESSAGE_REGEX,\n        MESSAGE_TYPE_CATEGORIZER,\n      )\n\n      generic_errors + pronto_infractions\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/r_spec.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Runs `rspec` test suite before push\n  #\n  # @see http://rspec.info/\n  module RSpec\n    def run\n      result = if @config['include']\n                 execute(command, args: applicable_files)\n               else\n                 execute(command)\n               end\n\n      return :pass if result.success?\n\n      output = result.stdout + result.stderr\n      [:fail, output]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/rake_target.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # runs specified rake targets. It fails on the first non-\n  # successful exit.\n  #\n  module RakeTarget\n    def run\n      targets = config['targets']\n\n      if Array(targets).empty?\n        raise 'RakeTarget: targets parameter is empty. Add at least one task to ' \\\n          'the targets parameter. Valid: Array of target names or String of ' \\\n          'target names'\n      end\n\n      targets.each do |task|\n        result = execute(command + [task])\n        unless result.success?\n          return :fail, \"Rake target #{task}:\\n#{result.stdout}\"\n        end\n      end\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/submodule_status.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all `SubmoduleStatus` hooks to notify the user if any\n  # submodules are uninitialized, out of date with the current index, or contain\n  # merge conflicts.\n  module SubmoduleStatus\n    def run\n      messages = []\n      submodule_statuses.each do |submodule_status|\n        path = submodule_status.path\n        if submodule_status.uninitialized?\n          messages << \"Submodule #{path} is uninitialized.\"\n        elsif submodule_status.outdated?\n          messages << \"Submodule #{path} is out of date with the current index.\"\n        elsif submodule_status.merge_conflict?\n          messages << \"Submodule #{path} has merge conflicts.\"\n        end\n      end\n\n      return :pass if messages.empty?\n\n      [:warn, messages.join(\"\\n\")]\n    end\n\n    private\n\n    def submodule_statuses\n      Overcommit::GitRepo.submodule_statuses(recursive: config['recursive'])\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook/shared/yarn_install.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Hook::Shared\n  # Shared code used by all YarnInstall hooks. Runs `yarn install` when a change\n  # is detected in the repository's dependencies.\n  #\n  # @see https://yarnpkg.com/\n  module YarnInstall\n    def run\n      result = execute(command)\n      return :fail, result.stderr unless result.success?\n\n      :pass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/base.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to the context with which a hook is being run.\n  #\n  # It acts as an adapter to the arguments passed to the hook, as well as\n  # context-specific information such as staged files, providing a single source\n  # of truth for this context.\n  #\n  # This is also important to house in a separate object so that any\n  # calculations can be memoized across all hooks in a single object, which\n  # helps with performance.\n  #\n  # @abstract\n  class Base\n    # Creates a hook context from the given configuration and input options.\n    #\n    # @param config [Overcommit::Configuration]\n    # @param args [Array<String>]\n    # @param input [IO] standard input stream\n    # @param options [Hash] cli options\n    def initialize(config, args, input, **options)\n      @config = config\n      @args = args\n      @input = input\n      @options = options\n    end\n\n    # Executes a command as if it were a regular git hook, passing all\n    # command-line arguments and the standard input stream.\n    #\n    # This is intended to be used by ad hoc hooks so developers can link up\n    # their existing git hooks with Overcommit.\n    def execute_hook(command)\n      Overcommit::Utils.execute(command, args: @args, input: input_string)\n    end\n\n    # Returns the camel-cased type of this hook (e.g. PreCommit)\n    #\n    # @return [String]\n    def hook_class_name\n      self.class.name.split('::').last\n    end\n\n    # Returns the snake-cased type of this hook (e.g. pre_commit)\n    #\n    # @return [String]\n    def hook_type_name\n      Overcommit::Utils.snake_case(hook_class_name)\n    end\n\n    # Returns the actual name of the hook script being run (e.g. pre-commit).\n    #\n    # @return [String]\n    def hook_script_name\n      hook_type_name.tr('_', '-')\n    end\n\n    # Initializes anything related to the environment.\n    #\n    # This is called before the hooks are run by the [HookRunner]. Different\n    # hook types can perform different setup.\n    def setup_environment\n      # Implemented by subclass, if applicable\n    end\n\n    # Resets the environment to an appropriate state.\n    #\n    # This is called after the hooks have been run by the [HookRunner].\n    # Different hook types can perform different cleanup operations, which are\n    # intended to \"undo\" the results of the call to {#setup_environment}.\n    def cleanup_environment\n      # Implemented by subclass, if applicable\n    end\n\n    # Returns a list of files that have been modified.\n    #\n    # By default, this returns an empty list. Subclasses should implement if\n    # there is a concept of files changing for the type of hook being run.\n    #\n    # @return [Array<String>]\n    def modified_files\n      []\n    end\n\n    # Returns the full list of files tracked by git\n    #\n    # @return [Array<String>]\n    def all_files\n      Overcommit::GitRepo.all_files\n    end\n\n    # Returns the contents of the entire standard input stream that were passed\n    # to the hook.\n    #\n    # @return [String]\n    def input_string\n      @input_string ||= @input.read\n    end\n\n    # Returns an array of lines passed to the hook via the standard input\n    # stream.\n    #\n    # @return [Array<String>]\n    def input_lines\n      @input_lines ||= input_string.split(\"\\n\")\n    end\n\n    # Returns a message to display on failure.\n    #\n    # @return [String]\n    def post_fail_message\n      nil\n    end\n\n    private\n\n    def filter_modified_files(modified_files)\n      filter_directories(filter_nonexistent(modified_files))\n    end\n\n    # Filter out non-existent files (unless it's a broken symlink, in which case\n    # it's a file that points to a non-existent file). This could happen if a\n    # file was renamed as part of an amendment, leading to the old file no\n    # longer existing.\n    def filter_nonexistent(modified_files)\n      modified_files.select do |file|\n        File.exist?(file) || Overcommit::Utils.broken_symlink?(file)\n      end\n    end\n\n    # Filter out directories. This could happen when changing a symlink to a\n    # directory as part of an amendment, since the symlink will still appear as\n    # a file, but the actual working tree will have a directory.\n    def filter_directories(modified_files)\n      modified_files.reject do |file|\n        File.directory?(file) && !Overcommit::Utils::FileUtils.symlink?(file)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/commit_msg.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative 'pre_commit'\nrequire_relative 'helpers/stash_unstaged_changes'\nrequire_relative 'helpers/file_modifications'\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by commit-msg hooks.\n  class CommitMsg < Base\n    include Overcommit::HookContext::Helpers::StashUnstagedChanges\n    include Overcommit::HookContext::Helpers::FileModifications\n\n    def empty_message?\n      commit_message.strip.empty?\n    end\n\n    # User commit message stripped of comments and diff (from verbose output).\n    def commit_message\n      commit_message_lines.join\n    end\n\n    # Updates the commit message to the specified text.\n    def update_commit_message(message)\n      ::File.open(commit_message_file, 'w') do |file|\n        file.write(message)\n      end\n    end\n\n    def commit_message_lines\n      raw_commit_message_lines.\n        take_while { |line| !line.start_with?('diff --git') }.\n        reject     { |line| line.start_with?(comment_character) }\n    end\n\n    def comment_character\n      @comment_character ||= Overcommit::GitConfig.comment_character\n    end\n\n    def commit_message_file\n      @args[0]\n    end\n\n    def post_fail_message\n      \"Failed commit message:\\n#{commit_message_lines.join.chomp}\\n\\n\" \\\n      \"Try again with your existing commit message by running:\\n\" \\\n      \"git commit --edit --file=#{commit_message_file}\"\n    end\n\n    private\n\n    def raw_commit_message_lines\n      ::IO.readlines(commit_message_file)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/diff.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/git_repo'\n\nrequire 'set'\n\nmodule Overcommit::HookContext\n  # Simulates a pre-commit context based on the diff with another git ref.\n  #\n  # This results in pre-commit hooks running against the changes between the current\n  # and another ref, which is useful for automated CI scripts.\n  class Diff < Base\n    def modified_files\n      @modified_files ||= Overcommit::GitRepo.modified_files(refs: @options[:diff])\n    end\n\n    def modified_lines_in_file(file)\n      @modified_lines ||= {}\n      @modified_lines[file] ||= Overcommit::GitRepo.extract_modified_lines(file,\n                                                                           refs: @options[:diff])\n    end\n\n    def hook_class_name\n      'PreCommit'\n    end\n\n    def hook_type_name\n      'pre_commit'\n    end\n\n    def hook_script_name\n      'pre-commit'\n    end\n\n    def initial_commit?\n      @initial_commit ||= Overcommit::GitRepo.initial_commit?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/helpers/file_modifications.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  module Helpers\n    # This module contains methods for determining what files were changed and on what unique line\n    # numbers did the change occur.\n    module FileModifications\n      # Returns whether this hook run was triggered by `git commit --amend`\n      def amendment?\n        return @amendment unless @amendment.nil?\n\n        cmd = Overcommit::Utils.parent_command\n        return unless cmd\n\n        amend_pattern = 'commit(\\s.*)?\\s--amend(\\s|$)'\n\n        # Since the ps command can return invalid byte sequences for commands\n        # containing unicode characters, we replace the offending characters,\n        # since the pattern we're looking for will consist of ASCII characters\n        unless cmd.valid_encoding?\n          cmd = Overcommit::Utils.\n            parent_command.\n            encode('UTF-16be', invalid: :replace, replace: '?').\n            encode('UTF-8')\n        end\n\n        return @amendment if\n          # True if the command is a commit with the --amend flag\n          @amendment = !(/\\s#{amend_pattern}/ =~ cmd).nil?\n\n        # Check for git aliases that call `commit --amend`\n        `git config --get-regexp \"^alias\\\\.\" \"#{amend_pattern}\"`.\n          scan(/alias\\.([-\\w]+)/). # Extract the alias\n          each do |match|\n            return @amendment if\n              # True if the command uses a git alias for `commit --amend`\n              @amendment = !(/git(\\.exe)?\\s+#{match[0]}/ =~ cmd).nil?\n          end\n\n        @amendment\n      end\n\n      # Get a list of added, copied, or modified files that have been staged.\n      # Renames and deletions are ignored, since there should be nothing to check.\n      def modified_files\n        unless @modified_files\n          currently_staged = Overcommit::GitRepo.modified_files(staged: true)\n          @modified_files = currently_staged\n\n          # Include files modified in last commit if amending\n          if amendment?\n            subcmd = 'show --format=%n'\n            previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)\n            @modified_files |= filter_modified_files(previously_modified)\n          end\n        end\n        @modified_files\n      end\n\n      # Returns the set of line numbers corresponding to the lines that were\n      # changed in a specified file.\n      def modified_lines_in_file(file)\n        @modified_lines ||= {}\n        unless @modified_lines[file]\n          @modified_lines[file] =\n            Overcommit::GitRepo.extract_modified_lines(file, staged: true)\n\n          # Include lines modified in last commit if amending\n          if amendment?\n            subcmd = 'show --format=%n'\n            @modified_lines[file] +=\n              Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)\n          end\n        end\n        @modified_lines[file]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  module Helpers\n    # This module contains behavior for stashing unstaged changes before hooks are ran and restoring\n    # them afterwards\n    module StashUnstagedChanges\n      # Stash unstaged contents of files so hooks don't see changes that aren't\n      # about to be committed.\n      def setup_environment\n        store_modified_times\n        Overcommit::GitRepo.store_merge_state\n        Overcommit::GitRepo.store_cherry_pick_state\n\n        # Don't attempt to stash changes if all changes are staged, as this\n        # prevents us from modifying files at all, which plays better with\n        # editors/tools which watch for file changes.\n        if !initial_commit? && unstaged_changes?\n          stash_changes\n\n          # While running hooks make it appear as if nothing changed\n          restore_modified_times\n        end\n      end\n\n      # Returns whether the current git branch is empty (has no commits).\n      def initial_commit?\n        return @initial_commit unless @initial_commit.nil?\n\n        @initial_commit = Overcommit::GitRepo.initial_commit?\n      end\n\n      # Restore unstaged changes and reset file modification times so it appears\n      # as if nothing ever changed.\n      #\n      # We want to restore the modification times for each of the files after\n      # every step to ensure as little time as possible has passed while the\n      # modification time on the file was newer. This helps us play more nicely\n      # with file watchers.\n      def cleanup_environment\n        if @changes_stashed\n          clear_working_tree\n          restore_working_tree\n          restore_modified_times\n        end\n\n        Overcommit::GitRepo.restore_merge_state\n        Overcommit::GitRepo.restore_cherry_pick_state\n      end\n\n      private\n\n      # Stores the modification times for all modified files to make it appear like\n      # they never changed.\n      #\n      # This prevents (some) editors from complaining about files changing when we\n      # stash changes before running the hooks.\n      def store_modified_times\n        @modified_times = {}\n\n        staged_files = modified_files\n        unstaged_files = Overcommit::GitRepo.modified_files(staged: false)\n\n        (staged_files + unstaged_files).each do |file|\n          next if Overcommit::Utils.broken_symlink?(file)\n          next unless File.exist?(file) # Ignore renamed files (old file no longer exists)\n\n          @modified_times[file] = File.mtime(file)\n        end\n      end\n\n      # Returns whether there are any changes to tracked files which have not yet\n      # been staged.\n      def unstaged_changes?\n        result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])\n        !result.success?\n      end\n\n      def stash_changes\n        @stash_attempted = true\n\n        stash_message = \"Overcommit: Stash of repo state before hook run at #{Time.now}\"\n        result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do\n          Overcommit::Utils.execute(\n            %w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]\n          )\n        end\n\n        unless result.success?\n          # Failure to stash in this case is likely due to a configuration\n          # issue (e.g. author/email not set or GPG signing key incorrect)\n          raise Overcommit::Exceptions::HookSetupFailed,\n                \"Unable to setup environment for #{hook_script_name} hook run:\" \\\n                \"\\nSTDOUT:#{result.stdout}\\nSTDERR:#{result.stderr}\"\n        end\n\n        @changes_stashed = `git stash list -1`.include?(stash_message)\n      end\n\n      # Restores the file modification times for all modified files to make it\n      # appear like they never changed.\n      def restore_modified_times\n        @modified_times.each do |file, time|\n          next if Overcommit::Utils.broken_symlink?(file)\n          next unless File.exist?(file)\n\n          File.utime(time, time, file)\n        end\n      end\n\n      # Clears the working tree so that the stash can be applied.\n      def clear_working_tree\n        removed_submodules = Overcommit::GitRepo.staged_submodule_removals\n\n        result = Overcommit::Utils.execute(%w[git reset --hard])\n        unless result.success?\n          raise Overcommit::Exceptions::HookCleanupFailed,\n                \"Unable to cleanup working tree after #{hook_script_name} hooks run:\" \\\n                \"\\nSTDOUT:#{result.stdout}\\nSTDERR:#{result.stderr}\"\n        end\n\n        # Hard-resetting a staged submodule removal results in the index being\n        # reset but the submodule being restored as an empty directory. This empty\n        # directory prevents us from stashing on a subsequent run if a hook fails.\n        #\n        # Work around this by removing these empty submodule directories as there\n        # doesn't appear any reason to keep them around.\n        removed_submodules.each do |submodule|\n          FileUtils.rmdir(submodule.path)\n        end\n      end\n\n      # Applies the stash to the working tree to restore the user's state.\n      def restore_working_tree\n        result = Overcommit::Utils.execute(%w[git stash pop --index])\n        unless result.success?\n          raise Overcommit::Exceptions::HookCleanupFailed,\n                \"Unable to restore working tree after #{hook_script_name} hooks run:\" \\\n                \"\\nSTDOUT:#{result.stdout}\\nSTDERR:#{result.stderr}\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/post_checkout.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by post-checkout\n  # hooks.\n  class PostCheckout < Base\n    # Returns the ref of the HEAD that we transitioned from.\n    def previous_head\n      @args[0]\n    end\n\n    # Returns the ref of the new current HEAD.\n    def new_head\n      @args[1]\n    end\n\n    # Returns whether this checkout was the result of changing/updating a\n    # branch.\n    def branch_checkout?\n      @args[2].to_i == 1\n    end\n\n    # Returns whether this checkout was for a single file.\n    def file_checkout?\n      !branch_checkout?\n    end\n\n    # Get a list of files that have been added or modified between\n    # `previous_head` and `new_head`. Renames and deletions are ignored, since\n    # there should be nothing to check.\n    def modified_files\n      @modified_files ||=\n        Overcommit::GitRepo.modified_files(refs: \"#{previous_head} #{new_head}\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/post_commit.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by post-commit\n  # hooks.\n  class PostCommit < Base\n    # Get a list of files that were added, copied, or modified in the last\n    # commit. Renames and deletions are ignored, since there should be nothing\n    # to check.\n    def modified_files\n      subcmd = 'show --format=%n'\n      @modified_files ||= Overcommit::GitRepo.modified_files(subcmd: subcmd)\n    end\n\n    # Returns the set of line numbers corresponding to the lines that were\n    # changed in a specified file.\n    def modified_lines_in_file(file)\n      subcmd = 'show --format=%n'\n      @modified_lines ||= {}\n      @modified_lines[file] ||=\n        Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)\n    end\n\n    # Returns whether the commit that triggered this hook is the first commit on\n    # the branch.\n    #\n    # @return [true,false]\n    def initial_commit?\n      return @initial_commit unless @initial_commit.nil?\n\n      @initial_commit = !Overcommit::Utils.execute(%w[git rev-parse HEAD~]).success?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/post_merge.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by post-merge\n  # hooks.\n  class PostMerge < Base\n    attr_accessor :args\n    # Get a list of files that were added, copied, or modified in the merge\n    # commit. Renames and deletions are ignored, since there should be nothing\n    # to check.\n    def modified_files\n      staged = squash?\n      refs = 'HEAD^ HEAD' if merge_commit?\n      @modified_files ||= Overcommit::GitRepo.modified_files(staged: staged, refs: refs)\n    end\n\n    # Returns the set of line numbers corresponding to the lines that were\n    # changed in a specified file.\n    def modified_lines_in_file(file)\n      staged = squash?\n      refs = 'HEAD^ HEAD' if merge_commit?\n      @modified_lines ||= {}\n      @modified_lines[file] ||=\n        Overcommit::GitRepo.extract_modified_lines(file, staged: staged, refs: refs)\n    end\n\n    # Returns whether this merge was made using --squash\n    def squash?\n      @args[0].to_i == 1\n    end\n\n    # Returns whether this merge was made without --squash\n    def merge_commit?\n      !squash?\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/post_rewrite.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers for contextual information used by post-rewrite hooks.\n  class PostRewrite < Base\n    # Returns whether this post-rewrite was triggered by `git commit --amend`.\n    #\n    # @return [true,false]\n    def amend?\n      @args[0] == 'amend'\n    end\n\n    # Returns whether this post-rewrite was triggered by `git rebase`.\n    #\n    # @return [true,false]\n    def rebase?\n      @args[0] == 'rebase'\n    end\n\n    # Returns the list of commits rewritten by the action that triggered this\n    # hook run.\n    #\n    # @return [Array<RewrittenCommit>]\n    def rewritten_commits\n      @rewritten_commits ||= input_lines.map do |line|\n        RewrittenCommit.new(*line.split(' '))\n      end\n    end\n\n    # Get a list of files that have been added or modified as part of a\n    # rewritten commit. Renames and deletions are ignored, since there should be\n    # nothing to check.\n    def modified_files\n      @modified_files ||= begin\n        @modified_files = []\n\n        rewritten_commits.each do |rewritten_commit|\n          refs = \"#{rewritten_commit.old_hash} #{rewritten_commit.new_hash}\"\n          @modified_files |= Overcommit::GitRepo.modified_files(refs: refs)\n        end\n\n        filter_modified_files(@modified_files)\n      end\n    end\n\n    # Struct encapsulating the old and new SHA1 hashes of a rewritten commit\n    RewrittenCommit = Struct.new(:old_hash, :new_hash)\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/pre_commit.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'fileutils'\nrequire 'set'\nrequire_relative 'helpers/stash_unstaged_changes'\nrequire_relative 'helpers/file_modifications'\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by pre-commit hooks.\n  #\n  # This includes staged files, which lines of those files have been modified,\n  # etc. It is also responsible for saving/restoring the state of the repo so\n  # hooks only inspect staged changes.\n  class PreCommit < Base\n    include Overcommit::HookContext::Helpers::StashUnstagedChanges\n    include Overcommit::HookContext::Helpers::FileModifications\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/pre_push.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by pre-push hooks.\n  class PrePush < Base\n    attr_accessor :args\n\n    def remote_name\n      @args[0]\n    end\n\n    def remote_url\n      @args[1]\n    end\n\n    def remote_ref_deletion?\n      return @remote_ref_deletion if defined?(@remote_ref_deletion)\n\n      @remote_ref_deletion ||= input_lines.\n                               first&.\n                               split(' ')&.\n                               first == '(deleted)'\n    end\n\n    def pushed_refs\n      input_lines.map do |line|\n        PushedRef.new(*line.split(' '))\n      end\n    end\n\n    def modified_files\n      @modified_files ||= pushed_refs.map(&:modified_files).flatten.uniq\n    end\n\n    def modified_lines_in_file(file)\n      @modified_lines ||= {}\n      @modified_lines[file] = pushed_refs.each_with_object(Set.new) do |pushed_ref, set|\n        set.merge(pushed_ref.modified_lines_in_file(file))\n      end\n    end\n\n    PushedRef = Struct.new(:local_ref, :local_sha1, :remote_ref, :remote_sha1) do\n      def forced?\n        !(created? || deleted? || overwritten_commits.empty?)\n      end\n\n      def created?\n        remote_sha1 == '0' * 40\n      end\n\n      def deleted?\n        local_sha1 == '0' * 40\n      end\n\n      def destructive?\n        deleted? || forced?\n      end\n\n      def modified_files\n        Overcommit::GitRepo.modified_files(refs: ref_range)\n      end\n\n      def modified_lines_in_file(file)\n        Overcommit::GitRepo.extract_modified_lines(file, refs: ref_range)\n      end\n\n      def to_s\n        \"#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\"\n      end\n\n      private\n\n      def ref_range\n        \"#{remote_sha1}..#{local_sha1}\"\n      end\n\n      def overwritten_commits\n        return @overwritten_commits if defined? @overwritten_commits\n\n        result = Overcommit::Subprocess.spawn(%W[git rev-list #{remote_sha1} ^#{local_sha1}])\n        if result.success?\n          result.stdout.split(\"\\n\")\n        else\n          raise Overcommit::Exceptions::GitRevListError,\n                \"Unable to check if commits on the remote ref will be overwritten: #{result.stderr}\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/pre_rebase.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by pre-rebase\n  # hooks.\n  class PreRebase < Base\n    # Returns the name of the branch we are rebasing onto.\n    def upstream_branch\n      @args[0]\n    end\n\n    # Returns the name of the branch being rebased. Empty if rebasing a\n    # detached HEAD.\n    def rebased_branch\n      @rebased_branch ||=\n        @args[1] || `git symbolic-ref --short --quiet HEAD`.chomp\n    end\n\n    # Returns whether we are rebasing a detached HEAD rather than a branch\n    def detached_head?\n      rebased_branch.empty?\n    end\n\n    # Returns whether this rebase is a fast-forward\n    def fast_forward?\n      rebased_commits.empty?\n    end\n\n    # Returns the SHA1-sums of the series of commits to be rebased\n    # in reverse topological order.\n    def rebased_commits\n      rebased_ref = detached_head? ? 'HEAD' : rebased_branch\n      @rebased_commits ||=\n        `git rev-list --topo-order --reverse #{upstream_branch}..#{rebased_ref}`.\n          split(\"\\n\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/prepare_commit_msg.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookContext\n  # Contains helpers related to contextual information used by prepare-commit-msg\n  # hooks.\n  class PrepareCommitMsg < Base\n    # Returns the name of the file that contains the commit log message\n    def commit_message_filename\n      @args[0]\n    end\n\n    # Returns the source of the commit message, and can be: message (if a -m or\n    # -F option was given); template (if a -t option was given or the\n    # configuration option commit.template is set); merge (if the commit is a\n    # merge or a .git/MERGE_MSG file exists); squash (if a .git/SQUASH_MSG file\n    # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend\n    # option was given)\n    def commit_message_source\n      @args[1]&.to_sym\n    end\n\n    # Returns the commit's SHA-1.\n    # If commit_message_source is :commit, it's passed through the command-line.\n    def commit_message_source_ref\n      @args[2] || `git rev-parse HEAD`\n    end\n\n    # Lock for the pre_commit_message file. Should be shared by all\n    # prepare-commit-message hooks\n    def lock\n      @lock ||= Monitor.new\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context/run_all.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'set'\n\nmodule Overcommit::HookContext\n  # Simulates a pre-commit context pretending that all files have been changed.\n  #\n  # This results in pre-commit hooks running against the entire repository,\n  # which is useful for automated CI scripts.\n  class RunAll < Base\n    def modified_files\n      @modified_files ||= all_files\n    end\n\n    # Returns all lines in the file since in this context the entire repo is\n    # being scrutinized.\n    #\n    # @param file [String]\n    # @return [Set]\n    def modified_lines_in_file(file)\n      @modified_lines_in_file ||= {}\n      @modified_lines_in_file[file] ||= Set.new(1..count_lines(file))\n    end\n\n    def hook_class_name\n      'PreCommit'\n    end\n\n    def hook_type_name\n      'pre_commit'\n    end\n\n    def hook_script_name\n      'pre-commit'\n    end\n\n    def initial_commit?\n      return @initial_commit unless @initial_commit.nil?\n\n      @initial_commit = Overcommit::GitRepo.initial_commit?\n    end\n\n    private\n\n    def count_lines(file)\n      File.foreach(file).count\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_context.rb",
    "content": "# frozen_string_literal: true\n\n# Utility module which manages the creation of {HookContext}s.\nmodule Overcommit::HookContext\n  def self.create(hook_type, config, args, input, **cli_options)\n    hook_type_class = Overcommit::Utils.camel_case(hook_type)\n    underscored_hook_type = Overcommit::Utils.snake_case(hook_type)\n\n    require \"overcommit/hook_context/#{underscored_hook_type}\"\n\n    Overcommit::HookContext.const_get(hook_type_class).new(config, args, input, **cli_options)\n  rescue LoadError, NameError => e\n    # Could happen when a symlink was created for a hook type Overcommit does\n    # not yet support.\n    raise Overcommit::Exceptions::HookContextLoadError,\n          \"Unable to load '#{hook_type}' hook context: '#{e}'\",\n          e.backtrace\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_loader/base.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookLoader\n  # Responsible for loading hooks from a file.\n  class Base\n    # @param config [Overcommit::Configuration]\n    # @param context [Overcommit::HookContext]\n    # @param logger [Overcommit::Logger]\n    def initialize(config, context, logger)\n      @config = config\n      @context = context\n      @log = logger\n    end\n\n    # When implemented in subclasses, loads the hooks for which that subclass is\n    # responsible.\n    #\n    # @return [Array<Hook>]\n    def load_hooks\n      raise NotImplementedError\n    end\n\n    private\n\n    attr_reader :log\n\n    # Load and return a {Hook} from a CamelCase hook name.\n    def create_hook(hook_name)\n      hook_type_class = Overcommit::Hook.const_get(@context.hook_class_name)\n      hook_base_class = hook_type_class.const_get(:Base)\n      hook_class = hook_type_class.const_get(hook_name)\n      unless hook_class < hook_base_class\n        raise Overcommit::Exceptions::HookLoadError,\n              \"Class #{hook_name} is not a subclass of #{hook_base_class}.\"\n      end\n\n      begin\n        Overcommit::Hook.const_get(@context.hook_class_name).\n                         const_get(hook_name).\n                         new(@config, @context)\n      rescue LoadError, NameError => e\n        raise Overcommit::Exceptions::HookLoadError,\n              \"Unable to load hook '#{hook_name}': #{e}\",\n              e.backtrace\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_loader/built_in_hook_loader.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::HookLoader\n  # Responsible for loading hooks that ship with Overcommit.\n  class BuiltInHookLoader < Base\n    def load_hooks\n      @config.enabled_builtin_hooks(@context).map do |hook_name|\n        underscored_hook_name = Overcommit::Utils.snake_case(hook_name)\n        require \"overcommit/hook/#{@context.hook_type_name}/#{underscored_hook_name}\"\n        create_hook(hook_name)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_loader/plugin_hook_loader.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'digest'\n\nmodule Overcommit::HookLoader\n  # Responsible for loading hooks that are specific to the repository Overcommit\n  # is running in.\n  class PluginHookLoader < Base\n    def load_hooks\n      check_for_modified_plugins if @config.verify_signatures?\n\n      hooks = plugin_paths.map do |plugin_path|\n        require plugin_path\n\n        hook_name = Overcommit::Utils.camel_case(File.basename(plugin_path, '.rb'))\n        create_hook(hook_name)\n      end\n\n      hooks + ad_hoc_hook_names.map do |hook_name|\n        create_ad_hoc_hook(hook_name)\n      end\n    end\n\n    def update_signatures\n      log.success('No plugin signatures have changed') if modified_plugins.empty?\n\n      modified_plugins.each do |plugin|\n        plugin.update_signature!\n        log.warning \"Updated signature of plugin #{plugin.hook_name}\"\n      end\n    end\n\n    private\n\n    def plugin_paths\n      directory = File.join(@config.plugin_directory, @context.hook_type_name)\n      Dir[File.join(directory, '*.rb')].sort\n    end\n\n    def plugin_hook_names\n      plugin_paths.map do |path|\n        Overcommit::Utils.camel_case(File.basename(path, '.rb'))\n      end\n    end\n\n    def ad_hoc_hook_names\n      @config.enabled_ad_hoc_hooks(@context)\n    end\n\n    def modified_plugins\n      (plugin_hook_names + ad_hoc_hook_names).\n        map { |hook_name| Overcommit::HookSigner.new(hook_name, @config, @context) }.\n        select(&:signature_changed?)\n    end\n\n    def check_for_modified_plugins\n      return if modified_plugins.empty?\n\n      log.bold_warning \"The following #{@context.hook_script_name} plugins \" \\\n                       'have been added, changed, or had their configuration modified:'\n      log.newline\n\n      modified_plugins.each do |signer|\n        log.warning \" * #{signer.hook_name} in #{signer.hook_path}\"\n      end\n\n      log.newline\n      log.bold_warning 'You should verify the changes and then run:'\n      log.newline\n      log.warning \"overcommit --sign #{@context.hook_script_name}\"\n      log.newline\n      log.log \"For more information, see #{Overcommit::REPO_URL}#security\"\n\n      raise Overcommit::Exceptions::InvalidHookSignature\n    end\n\n    def create_ad_hoc_hook(hook_name)\n      hook_module = Overcommit::Hook.const_get(@context.hook_class_name)\n      hook_base = hook_module.const_get('Base')\n\n      # Implement a simple class that executes the command and returns pass/fail\n      # based on the exit status\n      hook_class = Class.new(hook_base) do\n        def run\n          result = @context.execute_hook(command)\n\n          if result.success?\n            :pass\n          else\n            [:fail, result.stdout + result.stderr]\n          end\n        end\n      end\n\n      hook_module.const_set(hook_name, hook_class).new(@config, @context)\n    rescue LoadError, NameError => e\n      raise Overcommit::Exceptions::HookLoadError,\n            \"Unable to load hook '#{hook_name}': #{e}\",\n            e.backtrace\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_runner.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  # Responsible for loading the hooks the repository has configured and running\n  # them, collecting and displaying the results.\n  class HookRunner # rubocop:disable Metrics/ClassLength\n    # @param config [Overcommit::Configuration]\n    # @param logger [Overcommit::Logger]\n    # @param context [Overcommit::HookContext]\n    # @param printer [Overcommit::Printer]\n    def initialize(config, logger, context, printer)\n      @config = config\n      @log = logger\n      @context = context\n      @printer = printer\n      @hooks = []\n\n      @lock = Mutex.new\n      @resource = ConditionVariable.new\n      @slots_available = @config.concurrency\n    end\n\n    # Loads and runs the hooks registered for this {HookRunner}.\n    def run\n      # ASSUMPTION: we assume the setup and cleanup calls will never need to be\n      # interrupted, i.e. they will finish quickly. Should further evidence\n      # suggest this assumption does not hold, we will have to separately wrap\n      # these calls to allow some sort of \"are you sure?\" double-interrupt\n      # functionality, but until that's deemed necessary let's keep it simple.\n      InterruptHandler.isolate_from_interrupts do\n        # Load hooks before setting up the environment so that the repository\n        # has not been touched yet. This way any load errors at this point don't\n        # result in Overcommit leaving the repository in a bad state.\n        load_hooks\n\n        # Setup the environment without automatically calling\n        # `cleanup_environment` on an error. This is because it's possible that\n        # the `setup_environment` code did not fully complete, so there's no\n        # guarantee that `cleanup_environment` will be able to accomplish\n        # anything of value. The safest thing to do is therefore nothing in the\n        # unlikely case of failure.\n        @context.setup_environment\n\n        begin\n          run_hooks\n        ensure\n          @context.cleanup_environment\n        end\n      end\n    end\n\n    private\n\n    attr_reader :log\n\n    def run_hooks # rubocop:disable Metrics/MethodLength\n      if @hooks.any?(&:enabled?)\n        @printer.start_run\n\n        # Sort so hooks requiring fewer processors get queued first. This\n        # ensures we make better use of our available processors\n        @hooks_left = @hooks.sort_by { |hook| processors_for_hook(hook) }\n        @threads = Array.new(@config.concurrency) { Thread.new(&method(:consume)) }\n\n        begin\n          InterruptHandler.disable_until_finished_or_interrupted do\n            @threads.each(&:join)\n          end\n        rescue Interrupt\n          @printer.interrupt_triggered\n          # We received an interrupt on the main thread, so alert the\n          # remaining workers that an exception occurred\n          @interrupted = true\n          @threads.each { |thread| thread.raise Interrupt }\n        end\n\n        print_results\n\n        hook_failed = @failed || @interrupted\n\n        if hook_failed\n          message = @context.post_fail_message\n          @printer.hook_run_failed(message) unless message.nil?\n        end\n\n        !hook_failed\n      else\n        @printer.nothing_to_run\n        true # Run was successful\n      end\n    end\n\n    def consume\n      loop do\n        hook = @lock.synchronize { @hooks_left.pop }\n        break unless hook\n\n        run_hook(hook)\n      end\n    end\n\n    def wait_for_slot(hook)\n      @lock.synchronize do\n        slots_needed = processors_for_hook(hook)\n\n        loop do\n          if @slots_available >= slots_needed\n            @slots_available -= slots_needed\n\n            # Give another thread a chance since there are still slots available\n            @resource.signal if @slots_available > 0\n            break\n          elsif @slots_available > 0\n            # It's possible that another hook that requires fewer slots can be\n            # served, so give another a chance\n            @resource.signal\n\n            # Wait for a signal from another thread to try again\n            @resource.wait(@lock)\n          else\n            # Otherwise there are not slots left, so just wait for signal\n            @resource.wait(@lock)\n          end\n        end\n      end\n    end\n\n    def release_slot(hook)\n      @lock.synchronize do\n        slots_released = processors_for_hook(hook)\n        @slots_available += slots_released\n\n        # Signal every time in case there are threads that are already waiting for\n        # these slots to be released\n        @resource.signal\n      end\n    end\n\n    def processors_for_hook(hook)\n      hook.parallelize? ? hook.processors : @config.concurrency\n    end\n\n    def print_results\n      if @interrupted\n        @printer.run_interrupted\n      elsif @failed\n        @printer.run_failed\n      elsif @warned\n        @printer.run_warned\n      else\n        @printer.run_succeeded\n      end\n    end\n\n    def run_hook(hook) # rubocop:disable Metrics/CyclomaticComplexity\n      status, output = nil, nil\n\n      begin\n        wait_for_slot(hook)\n        return if should_skip?(hook)\n\n        status, output = hook.run_and_transform\n      rescue Overcommit::Exceptions::MessageProcessingError => e\n        status = :fail\n        output = e.message\n      rescue StandardError => e\n        status = :fail\n        output = \"Hook raised unexpected error\\n#{e.message}\\n#{e.backtrace.join(\"\\n\")}\"\n      end\n\n      @failed = true if status == :fail\n      @warned = true if status == :warn\n\n      @printer.end_hook(hook, status, output) unless @interrupted\n\n      status\n    rescue Interrupt\n      @interrupted = true\n    ensure\n      release_slot(hook)\n    end\n\n    def should_skip?(hook)\n      return true if @interrupted || !hook.enabled?\n\n      if hook.skip?\n        if hook.required?\n          @printer.required_hook_not_skipped(hook)\n        else\n          # Tell user if hook was skipped only if it actually would have run\n          @printer.hook_skipped(hook) if hook.run?\n          return true\n        end\n      end\n\n      !hook.run?\n    end\n\n    def load_hooks\n      require \"overcommit/hook/#{@context.hook_type_name}/base\"\n\n      @hooks += HookLoader::BuiltInHookLoader.new(@config, @context, @log).load_hooks\n\n      # Load plugin hooks after so they can subclass existing hooks\n      @hooks += HookLoader::PluginHookLoader.new(@config, @context, @log).load_hooks\n    rescue LoadError => e\n      # Include a more helpful message that will probably save some confusion\n      message = 'A load error occurred. ' +\n        if @config['gemfile']\n          \"Did you forget to specify a gem in your `#{@config['gemfile']}`?\"\n        else\n          'Did you forget to install a gem?'\n        end\n\n      raise Overcommit::Exceptions::HookLoadError,\n            \"#{message}\\n#{e.message}\",\n            e.backtrace\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/hook_signer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  # Calculates, stores, and retrieves stored signatures of hook plugins.\n  class HookSigner\n    attr_reader :hook_name\n\n    # We don't want to include the skip setting as it is set by Overcommit\n    # itself\n    IGNORED_CONFIG_KEYS = %w[skip].freeze\n\n    # @param hook_name [String] name of the hook\n    # @param config [Overcommit::Configuration]\n    # @param context [Overcommit::HookContext]\n    def initialize(hook_name, config, context)\n      @hook_name = hook_name\n      @config = config\n      @context = context\n    end\n\n    # Returns the path of the file that should be incorporated into this hooks\n    # signature.\n    #\n    # @return [String]\n    def hook_path\n      @hook_path ||= begin\n        plugin_path = File.join(@config.plugin_directory,\n                                @context.hook_type_name,\n                                \"#{Overcommit::Utils.snake_case(@hook_name)}.rb\")\n\n        if File.exist?(plugin_path)\n          plugin_path\n        else\n          # Otherwise this is an ad hoc hook using an existing hook script\n          hook_config = @config.for_hook(@hook_name, @context.hook_class_name)\n\n          command = Array(hook_config['command'] || hook_config['required_executable'])\n\n          if @config.verify_signatures? &&\n            signable_file?(command.first) &&\n            !Overcommit::GitRepo.tracked?(command.first)\n            raise Overcommit::Exceptions::InvalidHookDefinition,\n                  'Hook specified a `required_executable` or `command` that ' \\\n                  'is a path relative to the root of the repository, and so ' \\\n                  'must be tracked by Git in order to be signed'\n          end\n\n          File.join(Overcommit::Utils.repo_root, command.first.to_s)\n        end\n      end\n    end\n\n    def signable_file?(file)\n      return unless file\n\n      sep = Overcommit::OS.windows? ? '\\\\' : File::SEPARATOR\n      file.start_with?(\".#{sep}\") ||\n        file.start_with?(Overcommit::Utils.repo_root)\n    end\n\n    # Return whether the signature for this hook has changed since it was last\n    # calculated.\n    #\n    # @return [true,false]\n    def signature_changed?\n      signature != stored_signature\n    end\n\n    # Update the current stored signature for this hook.\n    def update_signature!\n      result = Overcommit::Utils.execute(\n        %w[git config --local] + [signature_config_key, signature]\n      )\n\n      unless result.success?\n        raise Overcommit::Exceptions::GitConfigError,\n              \"Unable to write to local repo git config: #{result.stderr}\"\n      end\n    end\n\n    private\n\n    # Calculates a hash of a hook using a combination of its configuration and\n    # file contents.\n    #\n    # This way, if either the plugin code changes or its configuration changes,\n    # the hash will change and we can alert the user to this change.\n    def signature\n      hook_config = @config.for_hook(@hook_name, @context.hook_class_name).\n                            dup.\n                            tap { |config| IGNORED_CONFIG_KEYS.each { |k| config.delete(k) } }\n\n      content_to_sign =\n        if signable_file?(hook_path) && Overcommit::GitRepo.tracked?(hook_path)\n          hook_contents\n        end\n\n      Digest::SHA256.hexdigest(content_to_sign.to_s + hook_config.to_s)\n    end\n\n    def hook_contents\n      File.read(hook_path)\n    end\n\n    def stored_signature\n      result = Overcommit::Utils.execute(\n        %w[git config --local --get] + [signature_config_key]\n      )\n\n      if result.status == 1 # Key doesn't exist\n        return ''\n      elsif result.status != 0\n        raise Overcommit::Exceptions::GitConfigError,\n              \"Unable to read from local repo git config: #{result.stderr}\"\n      end\n\n      result.stdout.chomp\n    end\n\n    def signature_config_key\n      \"overcommit.#{@context.hook_class_name}.#{@hook_name}.signature\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/installer.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'fileutils'\n\nmodule Overcommit\n  # Manages the installation of Overcommit hooks in a git repository.\n  class Installer # rubocop:disable Metrics/ClassLength\n    TEMPLATE_DIRECTORY = File.join(Overcommit::HOME, 'template-dir')\n    MASTER_HOOK = File.join(TEMPLATE_DIRECTORY, 'hooks', 'overcommit-hook')\n\n    def initialize(logger)\n      @log = logger\n    end\n\n    def run(target, options)\n      @target = target\n      @options = options\n      validate_target\n\n      case @options[:action]\n      when :uninstall then uninstall\n      when :update then update\n      else\n        install\n      end\n    end\n\n    private\n\n    attr_reader :log\n\n    def install\n      log.log \"Installing hooks into #{@target}\"\n\n      ensure_directory(hooks_path)\n      preserve_old_hooks\n      install_master_hook\n      install_hook_files\n      install_starter_config\n\n      # Auto-sign configuration file on install\n      config(verify: false).update_signature!\n\n      log.success \"Successfully installed hooks into #{@target}\"\n    end\n\n    def uninstall\n      log.log \"Removing hooks from #{@target}\"\n\n      uninstall_hook_files\n      uninstall_master_hook\n      restore_old_hooks\n\n      log.success \"Successfully removed hooks from #{@target}\"\n    end\n\n    # @return [true,false] whether the hooks were updated\n    def update\n      unless FileUtils.compare_file(MASTER_HOOK, master_hook_install_path)\n        preserve_old_hooks\n        install_master_hook\n        install_hook_files\n\n        log.success \"Hooks updated to Overcommit version #{Overcommit::VERSION}\"\n        true\n      end\n    end\n\n    def hooks_path\n      @hooks_path ||= Dir.chdir(@target) { GitConfig.hooks_path }\n    end\n\n    def old_hooks_path\n      File.join(hooks_path, 'old-hooks')\n    end\n\n    def master_hook_install_path\n      File.join(hooks_path, 'overcommit-hook')\n    end\n\n    def ensure_directory(path)\n      FileUtils.mkdir_p(path)\n    end\n\n    def validate_target\n      absolute_target = File.expand_path(@target)\n\n      unless File.directory?(absolute_target)\n        raise Overcommit::Exceptions::InvalidGitRepo, 'is not a directory'\n      end\n\n      git_dir_check = Dir.chdir(absolute_target) do\n        Overcommit::Utils.execute(%w[git rev-parse --git-dir])\n      end\n\n      unless git_dir_check.success?\n        raise Overcommit::Exceptions::InvalidGitRepo, 'does not appear to be a git repository'\n      end\n    end\n\n    def install_master_hook\n      FileUtils.mkdir_p(hooks_path)\n      FileUtils.cp(MASTER_HOOK, master_hook_install_path)\n    end\n\n    def uninstall_master_hook\n      FileUtils.rm_rf(master_hook_install_path, secure: true)\n    end\n\n    def install_hook_files\n      # Copy each hook type (pre-commit, commit-msg, etc.) from the master hook.\n      Dir.chdir(hooks_path) do\n        Overcommit::Utils.supported_hook_types.each do |hook_type|\n          unless can_replace_file?(hook_type)\n            raise Overcommit::Exceptions::PreExistingHooks,\n                  \"Hook '#{File.expand_path(hook_type)}' already exists and \" \\\n                  'was not installed by Overcommit'\n          end\n          FileUtils.rm_f(hook_type)\n          FileUtils.cp('overcommit-hook', hook_type)\n        end\n      end\n    end\n\n    def can_replace_file?(file)\n      @options[:force] ||\n        !File.exist?(file) ||\n        overcommit_hook?(file)\n    end\n\n    def preserve_old_hooks\n      return unless File.directory?(hooks_path)\n\n      ensure_directory(old_hooks_path)\n      Overcommit::Utils.supported_hook_types.each do |hook_type|\n        hook_file = File.join(hooks_path, hook_type)\n        unless can_replace_file?(hook_file)\n          log.warning \"Hook '#{File.expand_path(hook_type)}' already exists and \" \\\n                      \"was not installed by Overcommit. Moving to '#{old_hooks_path}'\"\n          FileUtils.mv(hook_file, old_hooks_path)\n        end\n      end\n      # Remove old-hooks directory if empty (i.e. no old hooks were preserved)\n      FileUtils.rmdir(old_hooks_path) if Dir.entries(old_hooks_path).size <= 2\n    end\n\n    def restore_old_hooks\n      return unless File.directory?(old_hooks_path)\n\n      log.log \"Restoring old hooks from #{old_hooks_path}\"\n\n      Dir.chdir(old_hooks_path) do\n        Overcommit::Utils.supported_hook_types.each do |hook_type|\n          FileUtils.mv(hook_type, hooks_path) if File.exist?(hook_type)\n        end\n      end\n      # Remove old-hooks directory if empty\n      FileUtils.rmdir(old_hooks_path)\n\n      log.success \"Successfully restored old hooks from #{old_hooks_path}\"\n    end\n\n    def uninstall_hook_files\n      return unless File.directory?(hooks_path)\n\n      Dir.chdir(hooks_path) do\n        Overcommit::Utils.supported_hook_types.each do |hook_type|\n          FileUtils.rm_rf(hook_type, secure: true) if overcommit_hook?(hook_type)\n        end\n      end\n    end\n\n    def install_starter_config\n      repo_config_file = File.join(@target, Overcommit::CONFIG_FILE_NAME)\n\n      return if File.exist?(repo_config_file)\n\n      FileUtils.cp(File.join(Overcommit::HOME, 'config', 'starter.yml'), repo_config_file)\n    end\n\n    def overcommit_hook?(file)\n      File.read(file) =~ /OVERCOMMIT_DISABLE/\n    rescue Errno::ENOENT\n      # Some Ruby implementations (e.g. JRuby) raise an error when the file\n      # doesn't exist. Standardize the behavior to return false.\n      false\n    end\n\n    # Returns the configuration for this repository.\n    def config(options = {})\n      Overcommit::ConfigurationLoader.new(log, options).load_repo_config\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/interrupt_handler.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'singleton'\n\n# Provides a handler for interrupt signals (SIGINT), allowing the application to\n# finish what it's currently working on.\nclass InterruptHandler\n  include Singleton\n\n  attr_accessor :isolate_signals, :signal_received, :reenable_on_interrupt\n\n  # Initialize safe interrupt signal handling.\n  def initialize\n    self.isolate_signals = false\n    self.signal_received = false\n    self.reenable_on_interrupt = false\n\n    Signal.trap('INT') do\n      if isolate_signals\n        self.signal_received = true\n      else\n        if reenable_on_interrupt\n          self.reenable_on_interrupt = false\n          self.isolate_signals = true\n        end\n\n        raise Interrupt # Allow interrupt to propagate to code\n      end\n    end\n  end\n\n  class << self\n    # Provide a way to allow a single Ctrl-C interrupt to happen and atomically\n    # re-enable interrupt protections once that interrupt is propagated.\n    #\n    # This prevents a race condition where code like the following:\n    #\n    #  begin\n    #    InterruptHandler.disable!\n    #    ... do stuff ...\n    #  rescue Interrupt\n    #    ... handle it ...\n    #  ensure\n    #    InterruptHandler.enable!\n    #  end\n    #\n    # ...could have the `enable!` call to the interrupt handler not called in\n    # the event another interrupt was received in between the interrupt being\n    # handled and the `ensure` block being entered.\n    #\n    # Thus you should always write:\n    #\n    #  begin\n    #    InterruptHandler.disable_until_finished_or_interrupted do\n    #      ... do stuff ...\n    #    end\n    #  rescue Interrupt\n    #    ... handle it ...\n    #  rescue\n    #    ... handle any other exceptions ...\n    #  end\n    def disable_until_finished_or_interrupted\n      instance.reenable_on_interrupt = true\n      instance.isolate_signals = false\n      yield\n    ensure\n      instance.isolate_signals = true\n    end\n\n    # Disable interrupt isolation.\n    def disable!\n      instance.isolate_signals = false\n    end\n\n    # Enable interrupt isolation.\n    def enable!\n      instance.isolate_signals = true\n    end\n\n    # Enable interrupt isolation while executing the provided block.\n    #\n    # @yield block to execute with interrupt isolation\n    def isolate_from_interrupts\n      instance.signal_received = false\n      instance.isolate_signals = true\n      result = yield\n      instance.isolate_signals = false\n      result\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/logger.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  # Encapsulates all communication to an output source.\n  class Logger\n    # Helper for creating a logger which outputs nothing.\n    def self.silent\n      new(File.open(File::NULL, 'w'))\n    end\n\n    # Creates a logger that will write to the given output stream.\n    #\n    # @param out [IO]\n    def initialize(out)\n      @out = out\n      @colorize =\n        if ENV.key?('OVERCOMMIT_COLOR')\n          !%w[0 false no].include?(ENV['OVERCOMMIT_COLOR'])\n        else\n          @out.tty?\n        end\n    end\n\n    # Write output without a trailing newline.\n    def partial(*args)\n      @out.print(*args)\n    end\n\n    # Prints a newline character (alias for readability).\n    def newline\n      log\n    end\n\n    # Flushes the [IO] object for partial lines\n    def flush\n      @out.flush if @out.respond_to? :flush\n    end\n\n    # Write a line of output.\n    #\n    # A newline character will always be appended.\n    def log(*args)\n      @out.puts(*args)\n    end\n\n    # Write a line of output if debug mode is enabled.\n    def debug(*args)\n      color('35', *args) unless ENV.fetch('OVERCOMMIT_DEBUG') { '' }.empty?\n    end\n\n    # Write a line of output that is intended to be emphasized.\n    def bold(*args)\n      color('1', *args)\n    end\n\n    # Write a line of output indicating a problem or error.\n    def error(*args)\n      color(31, *args)\n    end\n\n    # Write a line of output indicating a problem or error which is emphasized\n    # over a regular problem or error.\n    def bold_error(*args)\n      color('1;31', *args)\n    end\n\n    # Write a line of output indicating a successful or noteworthy event.\n    def success(*args)\n      color(32, *args)\n    end\n\n    # Write a line of output indicating a potential cause for concern, but not\n    # an actual error.\n    def warning(*args)\n      color(33, *args)\n    end\n\n    # Write a line of output indicating a potential cause for concern, but with\n    # greater emphasize compared to other warnings.\n    def bold_warning(*args)\n      color('1;33', *args)\n    end\n\n    private\n\n    # Outputs text wrapped in ANSI escape code necessary to produce a given\n    # color/text display.\n    #\n    # @param code [String] ANSI escape code, e.g. '1;33' for \"bold yellow\"\n    # @param str [String] string to wrap\n    # @param partial [true,false] whether to omit a newline\n    def color(code, str, partial = false)\n      send(partial ? :partial : :log,\n           @colorize ? \"\\033[#{code}m#{str}\\033[0m\" : str)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/message_processor.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit\n  # Utility class that encapsulates the handling of hook messages and whether\n  # they affect lines the user has modified or not.\n  #\n  # This class exposes an endpoint that extracts an appropriate hook/status\n  # output tuple from an array of {Overcommit::Hook::Message}s, respecting the\n  # configuration settings for the given hook.\n  class MessageProcessor\n    ERRORS_MODIFIED_HEADER = 'Errors on modified lines:'\n    WARNINGS_MODIFIED_HEADER = 'Warnings on modified lines:'\n    ERRORS_UNMODIFIED_HEADER = \"Errors on lines you didn't modify:\"\n    WARNINGS_UNMODIFIED_HEADER = \"Warnings on lines you didn't modify:\"\n    ERRORS_GENERIC_HEADER = 'Errors:'\n    WARNINGS_GENERIC_HEADER = 'Warnings:'\n\n    # @param hook [Overcommit::Hook::Base]\n    # @param unmodified_lines_setting [String] how to treat messages on\n    #   unmodified lines\n    def initialize(hook, unmodified_lines_setting)\n      @hook = hook\n      @setting = unmodified_lines_setting\n    end\n\n    # Returns a hook status/output tuple from the messages this processor was\n    # initialized with.\n    #\n    # @return [Array<Symbol,String>]\n    def hook_result(messages)\n      status, output = basic_status_and_output(messages)\n\n      # Nothing to do if there are no problems to begin with\n      return [status, output] if status == :pass\n\n      # Return as-is if this type of hook doesn't have the concept of modified lines\n      return [status, output] unless @hook.respond_to?(:modified_lines_in_file)\n\n      handle_modified_lines(messages, status)\n    end\n\n    private\n\n    def handle_modified_lines(messages, status)\n      messages = remove_ignored_messages(messages)\n\n      messages_with_line, generic_messages = messages.partition(&:line)\n\n      # Always print generic messages first\n      output = print_messages(\n        generic_messages,\n        ERRORS_GENERIC_HEADER,\n        WARNINGS_GENERIC_HEADER\n      )\n\n      messages_on_modified_lines, messages_on_unmodified_lines =\n        messages_with_line.partition { |message| message_on_modified_line?(message) }\n\n      output += print_messages(\n        messages_on_modified_lines,\n        ERRORS_MODIFIED_HEADER,\n        WARNINGS_MODIFIED_HEADER\n      )\n      output += print_messages(\n        messages_on_unmodified_lines,\n        ERRORS_UNMODIFIED_HEADER,\n        WARNINGS_UNMODIFIED_HEADER\n      )\n\n      [transform_status(status, generic_messages + messages_on_modified_lines), output]\n    end\n\n    def transform_status(status, messages_on_modified_lines)\n      # `report` indicates user wants the original status\n      return status if @setting == 'report'\n\n      error_messages, warning_messages =\n        messages_on_modified_lines.partition { |msg| msg.type == :error }\n\n      if can_upgrade_to_warning?(status, error_messages)\n        status = :warn\n      end\n\n      if can_upgrade_to_passing?(status, warning_messages)\n        status = :pass\n      end\n\n      status\n    end\n\n    def can_upgrade_to_warning?(status, error_messages)\n      status == :fail && error_messages.empty?\n    end\n\n    def can_upgrade_to_passing?(status, warning_messages)\n      status == :warn && @setting == 'ignore' && warning_messages.empty?\n    end\n\n    # Returns status and output for messages assuming no special treatment of\n    # messages occurring on unmodified lines.\n    def basic_status_and_output(messages)\n      status =\n        if messages.any? { |message| message.type == :error }\n          :fail\n        elsif messages.any? { |message| message.type == :warning }\n          :warn\n        else\n          :pass\n        end\n\n      output = ''\n      if messages.any?\n        output += messages.join(\"\\n\") + \"\\n\"\n      end\n\n      [status, output]\n    end\n\n    def print_messages(messages, error_heading, warning_heading)\n      output = ''\n      errors, warnings = messages.partition { |msg| msg.type == :error }\n\n      if errors.any?\n        output += \"#{error_heading}\\n#{errors.join(\"\\n\")}\\n\"\n      end\n\n      if warnings.any?\n        output += \"#{warning_heading}\\n#{warnings.join(\"\\n\")}\\n\"\n      end\n\n      output\n    end\n\n    def remove_ignored_messages(messages)\n      # If user wants to ignore messages on unmodified lines, simply remove them\n      return messages unless @setting == 'ignore'\n\n      messages.select { |message| message_on_modified_line?(message) }\n    end\n\n    def message_on_modified_line?(message)\n      # Message without line number assumed to apply to entire file\n      return true unless message.line\n\n      @hook.modified_lines_in_file(message.file).include?(message.line)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/os.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'rbconfig'\n\nmodule Overcommit\n  # Methods relating to the current operating system\n  module OS\n    class << self\n      def windows?\n        !(/mswin|msys|mingw|bccwin|wince|emc/ =~ host_os).nil?\n      end\n\n      def cygwin?\n        !(/cygwin/ =~ host_os).nil?\n      end\n\n      def mac?\n        !(/darwin|mac os/ =~ host_os).nil?\n      end\n\n      def unix?\n        !windows?\n      end\n\n      def linux?\n        unix? && !mac? && !cygwin?\n      end\n\n      private\n\n      def host_os\n        @host_os ||= ::RbConfig::CONFIG['host_os'].freeze\n      end\n    end\n\n    SEPARATOR = (windows? ? '\\\\' : File::SEPARATOR).freeze\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/printer.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'monitor'\n\nmodule Overcommit\n  # Provide a set of callbacks which can be executed as events occur during the\n  # course of {HookRunner#run}.\n  class Printer\n    attr_reader :log\n\n    def initialize(config, logger, context)\n      @config = config\n      @log = logger\n      @context = context\n      @lock = Monitor.new # Need to use monitor so we can have re-entrant locks\n      synchronize_all_methods\n    end\n\n    # Executed at the very beginning of running the collection of hooks.\n    def start_run\n      log.bold \"Running #{hook_script_name} hooks\" unless @config['quiet']\n    end\n\n    def nothing_to_run\n      log.debug \"✓ No applicable #{hook_script_name} hooks to run\"\n    end\n\n    def hook_skipped(hook)\n      log.warning \"Skipping #{hook.name}\"\n    end\n\n    def required_hook_not_skipped(hook)\n      log.warning \"Cannot skip #{hook.name} since it is required\"\n    end\n\n    # Executed at the end of an individual hook run.\n    def end_hook(hook, status, output)\n      # Want to print the header for quiet hooks only if the result wasn't good\n      # so that the user knows what failed\n      print_header(hook) if (!hook.quiet? && !@config['quiet']) || status != :pass\n\n      print_result(hook, status, output)\n    end\n\n    def interrupt_triggered\n      log.error \"\\nInterrupt signal received. Stopping hooks...\"\n    end\n\n    # Executed when a hook run was interrupted/cancelled by user.\n    def run_interrupted\n      log.newline\n      log.warning '⚠  Hook run interrupted by user'\n      log.warning \"⚠  If files appear modified/missing, check your stash to recover them\\n\"\n    end\n\n    # Executed when one or more hooks by the end of the run.\n    def run_failed\n      log.newline\n      log.error \"✗ One or more #{hook_script_name} hooks failed\"\n      log.newline\n    end\n\n    # Executed when no hooks failed by the end of the run, but some warned.\n    def run_warned\n      log.newline\n      log.warning \"⚠ All #{hook_script_name} hooks passed, but with warnings\"\n      log.newline\n    end\n\n    # Executed when no hooks failed by the end of the run.\n    def run_succeeded\n      unless @config['quiet']\n        log.newline\n        log.success \"✓ All #{hook_script_name} hooks passed\"\n        log.newline\n      end\n    end\n\n    def hook_run_failed(message)\n      log.newline\n      log.log message\n      log.newline\n    end\n\n    private\n\n    def print_header(hook)\n      hook_name = \"[#{hook.name}] \"\n      log.partial hook.description\n      log.partial '.' * [70 - hook.description.length - hook_name.length, 0].max\n      log.partial hook_name\n      log.flush\n    end\n\n    def print_result(hook, status, output) # rubocop:disable Metrics/CyclomaticComplexity\n      case status\n      when :pass\n        log.success 'OK' unless @config['quiet'] || hook.quiet?\n      when :warn\n        log.warning 'WARNING'\n        print_report(output, :bold_warning)\n      when :fail\n        log.error 'FAILED'\n        print_report(output, :bold_error)\n      when :interrupt\n        log.error 'INTERRUPTED'\n        print_report(output, :bold_error)\n      else\n        log.error '???'\n        print_report(\"Hook returned unknown status `#{status.inspect}` -- ignoring.\",\n                     :bold_error)\n      end\n    end\n\n    def print_report(output, format = :log)\n      log.send(format, output) unless output.nil? || output.empty?\n    end\n\n    def hook_script_name\n      @context.hook_script_name\n    end\n\n    # Get all public methods that were defined on this class and wrap them with\n    # synchronization locks so we ensure the output isn't interleaved amongst\n    # the various threads.\n    def synchronize_all_methods\n      methods = self.class.instance_methods - self.class.superclass.instance_methods\n\n      methods.each do |method_name|\n        old_method = :\"old_#{method_name}\"\n        new_method = :\"synchronized_#{method_name}\"\n\n        self.class.__send__(:alias_method, old_method, method_name)\n\n        self.class.send(:define_method, new_method) do |*args|\n          @lock.synchronize { __send__(old_method, *args) }\n        end\n\n        self.class.__send__(:alias_method, method_name, new_method)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/subprocess.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'childprocess'\nrequire 'tempfile'\nrequire 'overcommit/os'\n\nmodule Overcommit\n  # Manages execution of a child process, collecting the exit status and\n  # standard out/error output.\n  class Subprocess\n    # Encapsulates the result of a process.\n    #\n    # @attr_reader status [Integer] exit status code returned by process\n    # @attr_reader stdout [String] standard output stream output\n    # @attr_reader stderr [String] standard error stream output\n    Result = Struct.new(:status, :stdout, :stderr) do\n      def success?\n        status == 0\n      end\n    end\n\n    class << self\n      # Spawns a new process using the given array of arguments (the first\n      # element is the command).\n      #\n      # @param args [Array<String>]\n      # @param options [Hash]\n      # @option options [String] input string to pass via standard input stream\n      # @return [Result]\n      def spawn(args, options = {})\n        args = win32_prepare_args(args) if OS.windows?\n\n        process = ChildProcess.build(*args)\n\n        out, err = assign_output_streams(process)\n\n        process.duplex = true if options[:input] # Make stdin available if needed\n        process.start\n        if options[:input]\n          begin\n            process.io.stdin.puts(options[:input])\n          rescue StandardError\n            # Silently ignore if the standard input stream of the spawned\n            # process is closed before we get a chance to write to it. This\n            # happens on JRuby a lot.\n          ensure\n            process.io.stdin.close\n          end\n        end\n        process.wait\n\n        err.rewind\n        out.rewind\n\n        Result.new(process.exit_code, out.read, err.read)\n      end\n\n      # Spawns a new process in the background using the given array of\n      # arguments (the first element is the command).\n      def spawn_detached(args)\n        args = win32_prepare_args(args) if OS.windows?\n\n        process = ChildProcess.build(*args)\n        process.detach = true\n\n        assign_output_streams(process)\n\n        process.start\n      end\n\n      private\n\n      # Necessary to run commands in the cmd.exe context.\n      # Args are joined to properly handle quotes and special characters.\n      def win32_prepare_args(args)\n        args = args.map do |arg|\n          # Quote args that contain whitespace\n          arg = \"\\\"#{arg}\\\"\" if arg =~ /\\s/\n\n          # Escape cmd.exe metacharacters\n          arg.gsub(/[()%!^\"<>&|]/, '^\\0')\n        end\n\n        %w[cmd.exe /c] + [args.join(' ')]\n      end\n\n      # @param process [ChildProcess]\n      # @return [Array<IO>]\n      def assign_output_streams(process)\n        %w[out err].map do |stream_name|\n          ::Tempfile.new(stream_name).tap do |stream|\n            stream.sync = true\n            process.io.send(\"std#{stream_name}=\", stream)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/utils/file_utils.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/os'\nrequire 'overcommit/subprocess'\n\nmodule Overcommit::Utils\n  # Utility functions for file IO.\n  module FileUtils\n    class << self\n      # When the host OS is Windows, uses the `mklink` command to create an\n      # NTFS symbolic link from `new_name` to `old_name`. Otherwise delegates\n      # to `File.symlink`\n      def symlink(old_name, new_name)\n        return File.symlink(old_name, new_name) unless Overcommit::OS.windows?\n\n        result = win32_mklink_cmd(old_name, new_name)\n        result.status\n      end\n\n      # When the host OS is Windows, uses the `dir` command to check whether\n      # `file_name` is an NTFS symbolic link. Otherwise delegates to\n      # `File.symlink`.\n      def symlink?(file_name)\n        return File.symlink?(file_name) unless Overcommit::OS.windows?\n\n        result = win32_dir_cmd(file_name)\n        win32_symlink?(result.stdout)\n      end\n\n      # When the host OS is Windows, uses the `dir` command to check whether\n      # `link_name` is an NTFS symbolic link. If so, it parses the target from\n      # the command output. Otherwise raises an `ArgumentError`. Delegates to\n      # `File.readlink` if the host OS is not Windows.\n      def readlink(link_name)\n        return File.readlink(link_name) unless Overcommit::OS.windows?\n\n        result = win32_dir_cmd(link_name)\n\n        unless win32_symlink?(result.stdout)\n          raise ArgumentError, \"#{link_name} is not a symlink\"\n        end\n\n        # Extract symlink target from output, which looks like:\n        #   11/13/2012 12:53 AM <SYMLINK> mysymlink [C:\\Windows\\Temp\\somefile.txt]\n        result.stdout[/\\[(.+)\\]/, 1]\n      end\n\n      private\n\n      def win32_dir_cmd(file_name)\n        Overcommit::Subprocess.spawn(\n          %W[dir #{win32_fix_pathsep(file_name)}]\n        )\n      end\n\n      def win32_mklink_cmd(old_name, new_name)\n        Overcommit::Subprocess.spawn(\n          %W[mklink #{win32_fix_pathsep(new_name)} #{win32_fix_pathsep(old_name)}]\n        )\n      end\n\n      def win32_fix_pathsep(path)\n        path.tr(File::SEPARATOR, Overcommit::OS::SEPARATOR)\n      end\n\n      def win32_symlink?(dir_output)\n        !(dir_output =~ /<SYMLINK>/).nil?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/utils/messages_utils.rb",
    "content": "# frozen_string_literal: true\n\nmodule Overcommit::Utils\n  # Utility to process messages\n  module MessagesUtils\n    class << self\n      # Extract file, line number, and type of message from an error/warning\n      # messages in output.\n      #\n      # Assumes each element of `output` is a separate error/warning with all\n      # information necessary to identify it.\n      #\n      # @param output_messages [Array<String>] unprocessed error/warning messages\n      # @param regex [Regexp] regular expression defining `file`, `line` and\n      #   `type` capture groups used to extract file locations and error/warning\n      #   type from each line of output\n      # @param type_categorizer [Proc] function executed against the `type`\n      #   capture group to convert it to a `:warning` or `:error` symbol. Assumes\n      #   `:error` if `nil`.\n      # @raise [Overcommit::Exceptions::MessageProcessingError] line of output did\n      #   not match regex\n      # @return [Array<Message>]\n      def extract_messages(output_messages, regex, type_categorizer = nil)\n        output_messages.map.with_index do |message, index|\n          unless match = message.match(regex)\n            raise Overcommit::Exceptions::MessageProcessingError,\n                  'Unexpected output: unable to determine line number or type ' \\\n                  \"of error/warning for output:\\n\" \\\n                  \"#{output_messages[index..].join(\"\\n\")}\"\n          end\n\n          file = extract_file(match, message)\n          line = extract_line(match, message) if match.names.include?('line') && match[:line]\n          type = extract_type(match, message, type_categorizer)\n\n          Overcommit::Hook::Message.new(type, file, line, message)\n        end\n      end\n\n      private\n\n      def extract_file(match, message)\n        return unless match.names.include?('file')\n\n        if match[:file].to_s.empty?\n          raise Overcommit::Exceptions::MessageProcessingError,\n                \"Unexpected output: no file found in '#{message}'\"\n        end\n\n        match[:file]\n      end\n\n      def extract_line(match, message)\n        return unless match.names.include?('line')\n\n        Integer(match[:line])\n      rescue ArgumentError, TypeError\n        raise Overcommit::Exceptions::MessageProcessingError,\n              \"Unexpected output: invalid line number found in '#{message}'\"\n      end\n\n      def extract_type(match, message, type_categorizer)\n        if type_categorizer\n          type_match = match.names.include?('type') ? match[:type] : nil\n          type = type_categorizer.call(type_match)\n          unless Overcommit::Hook::MESSAGE_TYPES.include?(type)\n            raise Overcommit::Exceptions::MessageProcessingError,\n                  \"Invalid message type '#{type}' for '#{message}': must \" \\\n                  \"be one of #{Overcommit::Hook::MESSAGE_TYPES.inspect}\"\n          end\n          type\n        else\n          :error # Assume error since no categorizer was defined\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/utils.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'pathname'\nrequire 'overcommit/os'\nrequire 'overcommit/subprocess'\nrequire 'overcommit/command_splitter'\nrequire 'tempfile'\n\nmodule Overcommit\n  # Utility functions for general use.\n  module Utils\n    # Helper class for doing quick constraint validations on version numbers.\n    #\n    # This allows us to execute code based on the git version.\n    class Version < Gem::Version\n      # Overload comparison operators so we can conveniently compare this\n      # version directly to a string in code.\n      %w[< <= > >= == !=].each do |operator|\n        define_method operator do |version|\n          case version\n          when String\n            super(Gem::Version.new(version))\n          else\n            super(version)\n          end\n        end\n      end\n    end\n\n    class << self\n      # @return [Overcommit::Logger] logger with which to send debug output\n      attr_accessor :log\n\n      def script_path(script)\n        File.join(Overcommit::HOME, 'libexec', script)\n      end\n\n      # Returns an absolute path to the root of the repository.\n      #\n      # We do this ourselves rather than call `git rev-parse --show-toplevel` to\n      # solve an issue where the .git directory might not actually be valid in\n      # tests.\n      #\n      # @return [String]\n      def repo_root\n        @repo_root ||=\n          begin\n            result = execute(%w[git rev-parse --show-toplevel])\n            unless result.success?\n              raise Overcommit::Exceptions::InvalidGitRepo,\n                    'Unable to determine location of GIT_DIR. ' \\\n                    'Not a recognizable Git repository!'\n            end\n            result.stdout.chomp(\"\\n\")\n          end\n      end\n\n      # Returns an absolute path to the .git directory for a repo.\n      #\n      # @return [String]\n      def git_dir\n        @git_dir ||=\n          begin\n            cmd = %w[git rev-parse]\n            cmd << (GIT_VERSION < '2.5' ? '--git-dir' : '--git-common-dir')\n            result = execute(cmd)\n            unless result.success?\n              raise Overcommit::Exceptions::InvalidGitRepo,\n                    'Unable to determine location of GIT_DIR. ' \\\n                    'Not a recognizable Git repository!'\n            end\n            File.expand_path(result.stdout.chomp(\"\\n\"), Dir.pwd)\n          end\n      end\n\n      # Remove ANSI escape sequences from a string.\n      #\n      # This is useful for stripping colorized output from external tools.\n      #\n      # @param text [String]\n      # @return [String]\n      def strip_color_codes(text)\n        text.gsub(/\\e\\[(\\d+)(;\\d+)*m/, '')\n      end\n\n      # Shamelessly stolen from:\n      # stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby\n      def snake_case(str)\n        str.gsub(/::/, '/').\n            gsub(/([A-Z]+)([A-Z][a-z])/, '\\1_\\2').\n            gsub(/([a-z\\d])([A-Z])/, '\\1_\\2').\n            tr('-', '_').\n            downcase\n      end\n\n      # Converts a string containing underscores/hyphens/spaces into CamelCase.\n      def camel_case(str)\n        str.split(/_|-| /).map { |part| part.sub(/^\\w/, &:upcase) }.join\n      end\n\n      # Returns a list of supported hook types (pre-commit, commit-msg, etc.)\n      def supported_hook_types\n        Dir[File.join(HOOK_DIRECTORY, '*')].\n          select { |file| File.directory?(file) }.\n          reject { |file| File.basename(file) == 'shared' }.\n          map { |file| File.basename(file).tr('_', '-') }\n      end\n\n      # Returns a list of supported hook classes (PreCommit, CommitMsg, etc.)\n      def supported_hook_type_classes\n        supported_hook_types.map do |file|\n          file.split('-').map(&:capitalize).join\n        end\n      end\n\n      # @param cmd [String]\n      # @return [true,false] whether a command can be found given the current\n      #   environment path.\n      def in_path?(cmd)\n        # ENV['PATH'] doesn't include the repo root, but that is a valid\n        # location for executables, so we want to add it to the list of places\n        # we are checking for the executable.\n        paths = [repo_root] + ENV['PATH'].split(File::PATH_SEPARATOR)\n        exts  = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']\n        paths.each do |path|\n          exts.each do |ext|\n            cmd_with_ext = cmd.upcase.end_with?(ext.upcase) ? cmd : \"#{cmd}#{ext}\"\n            full_path = File.join(path, cmd_with_ext)\n            return true if File.executable?(full_path)\n          end\n        end\n        false\n      end\n\n      # Return the parent command that triggered this hook run\n      #\n      # @return [String,nil] the command as a string, if a parent exists.\n      def parent_command\n        # When run in Docker containers, there may be no parent process.\n        return if Process.ppid.zero?\n\n        if OS.windows?\n          `wmic process where ProcessId=#{Process.ppid} get CommandLine /FORMAT:VALUE`.\n            strip.\n            slice(/(?<=CommandLine=).+/)\n        elsif OS.cygwin?\n          # Cygwin's `ps` command behaves differently than the traditional\n          # Linux version, but a comparable `procps` is provided to compensate.\n          `procps -ocommand= -p #{Process.ppid}`.chomp\n        else\n          `ps -ocommand= -p #{Process.ppid}`.chomp\n        end\n      end\n\n      # Execute a command in a subprocess, capturing exit status and output from\n      # both standard and error streams.\n      #\n      # This is intended to provide a centralized place to perform any checks or\n      # filtering of the command before executing it.\n      #\n      # The `args` option provides a convenient way of splitting up long\n      # argument lists which would otherwise exceed the maximum command line\n      # length of the OS. It will break up the list into chunks and run the\n      # command with the same prefix `initial_args`, finally combining the\n      # output together at the end.\n      #\n      # This requires that the external command you are running can have its\n      # work split up in this way and still produce the same resultant output\n      # when outputs of the individual commands are concatenated back together.\n      #\n      # @param initial_args [Array<String>]\n      # @param options [Hash]\n      # @option options [Array<String>] :args long list of arguments to split up\n      # @return [Overcommit::Subprocess::Result] status, stdout, and stderr\n      def execute(initial_args, options = {})\n        if initial_args.include?('|')\n          raise Overcommit::Exceptions::InvalidCommandArgs,\n                'Cannot pipe commands with the `execute` helper'\n        end\n\n        result =\n          if (splittable_args = options.fetch(:args) { [] }).any?\n            debug(initial_args.join(' ') + \" ... (#{splittable_args.length} splittable args)\")\n            Overcommit::CommandSplitter.execute(initial_args, options)\n          else\n            debug(initial_args.join(' '))\n            Overcommit::Subprocess.spawn(initial_args, options)\n          end\n\n        debug(\"EXIT STATUS: #{result.status}\")\n        debug(\"STDOUT: #{result.stdout.inspect}\")\n        debug(\"STDERR: #{result.stderr.inspect}\")\n\n        result\n      end\n\n      # Execute a command in a subprocess, returning immediately.\n      #\n      # This provides a convenient way to execute long-running processes for\n      # which we do not need to know the result.\n      #\n      # @param args [Array<String>]\n      # @return [ChildProcess] detached process spawned in the background\n      def execute_in_background(args)\n        if args.include?('|')\n          raise Overcommit::Exceptions::InvalidCommandArgs,\n                'Cannot pipe commands with the `execute_in_background` helper'\n        end\n\n        debug(\"Spawning background task: #{args.join(' ')}\")\n        Subprocess.spawn_detached(args)\n      end\n\n      # Return the number of processors used by the OS for process scheduling.\n      #\n      # @see https://github.com/grosser/parallel/blob/v1.6.1/lib/parallel/processor_count.rb#L17-L51\n      def processor_count # rubocop:disable all\n        @processor_count ||=\n          begin\n            if Overcommit::OS.windows?\n              require 'win32ole'\n              result = WIN32OLE.connect('winmgmts://').ExecQuery(\n                'select NumberOfLogicalProcessors from Win32_Processor'\n              )\n              result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)\n            elsif File.readable?('/proc/cpuinfo')\n              IO.read('/proc/cpuinfo').scan(/^processor/).size\n            elsif File.executable?('/usr/bin/hwprefs')\n              IO.popen('/usr/bin/hwprefs thread_count').read.to_i\n            elsif File.executable?('/usr/sbin/psrinfo')\n              IO.popen('/usr/sbin/psrinfo').read.scan(/^.*on-*line/).size\n            elsif File.executable?('/usr/sbin/ioscan')\n              IO.popen('/usr/sbin/ioscan -kC processor') do |out|\n                out.read.scan(/^.*processor/).size\n              end\n            elsif File.executable?('/usr/sbin/pmcycles')\n              IO.popen('/usr/sbin/pmcycles -m').read.count(\"\\n\")\n            elsif File.executable?('/usr/sbin/lsdev')\n              IO.popen('/usr/sbin/lsdev -Cc processor -S 1').read.count(\"\\n\")\n            elsif File.executable?('/usr/sbin/sysctl')\n              IO.popen('/usr/sbin/sysctl -n hw.ncpu').read.to_i\n            elsif File.executable?('/sbin/sysctl')\n              IO.popen('/sbin/sysctl -n hw.ncpu').read.to_i\n            else\n              # Unknown platform; assume 1 processor\n              1\n            end\n          end\n      end\n\n      # Calls a block of code with a modified set of environment variables,\n      # restoring them once the code has executed.\n      def with_environment(env)\n        old_env = {}\n        env.each do |var, value|\n          old_env[var] = ENV[var.to_s]\n          ENV[var.to_s] = value\n        end\n\n        yield\n      ensure\n        old_env.each { |var, value| ENV[var.to_s] = value }\n      end\n\n      # Returns whether a file is a broken symlink.\n      #\n      # @return [true,false]\n      def broken_symlink?(file)\n        # JRuby's implementation of File.exist? returns true for broken\n        # symlinks, so we need use File.size?\n        Overcommit::Utils::FileUtils.symlink?(file) && File.size?(file).nil?\n      end\n\n      # Convert a glob pattern to an absolute path glob pattern rooted from the\n      # repository root directory.\n      #\n      # @param glob [String]\n      # @return [String]\n      def convert_glob_to_absolute(glob)\n        File.join(repo_root, glob)\n      end\n\n      # Return whether a pattern matches the given path.\n      #\n      # @param pattern [String]\n      # @param path [String]\n      def matches_path?(pattern, path)\n        File.fnmatch?(\n          pattern, path,\n          File::FNM_PATHNAME | # Wildcard doesn't match separator\n          File::FNM_DOTMATCH   # Wildcards match dotfiles\n        )\n      end\n\n      private\n\n      # Log debug output.\n      #\n      # This is necessary since some specs indirectly call utility functions but\n      # don't explicitly set the logger for the Utils class, so we do a quick\n      # check here to see if it's set before we attempt to log.\n      #\n      # @param args [Array<String>]\n      def debug(*args)\n        log&.debug(*args)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/overcommit/version.rb",
    "content": "# frozen_string_literal: true\n\n# Defines the gem version.\nmodule Overcommit\n  VERSION = '0.68.0'\nend\n"
  },
  {
    "path": "lib/overcommit.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'overcommit/os'\nrequire 'overcommit/constants'\nrequire 'overcommit/exceptions'\nrequire 'overcommit/utils/file_utils'\nrequire 'overcommit/utils'\nrequire 'overcommit/git_version'\nrequire 'overcommit/configuration_validator'\nrequire 'overcommit/configuration'\nrequire 'overcommit/configuration_loader'\nrequire 'overcommit/hook/base'\nrequire 'overcommit/hook_context/base'\nrequire 'overcommit/hook_context'\nrequire 'overcommit/git_config'\nrequire 'overcommit/git_repo'\nrequire 'overcommit/hook_signer'\nrequire 'overcommit/hook_loader/base'\nrequire 'overcommit/hook_loader/built_in_hook_loader'\nrequire 'overcommit/hook_loader/plugin_hook_loader'\nrequire 'overcommit/interrupt_handler'\nrequire 'overcommit/printer'\nrequire 'overcommit/hook_runner'\nrequire 'overcommit/installer'\nrequire 'overcommit/logger'\nrequire 'overcommit/version'\n"
  },
  {
    "path": "libexec/gerrit-change-id",
    "content": "#!/bin/sh\n# From Gerrit Code Review 2.5.1\n#\n# Part of Gerrit Code Review (http://code.google.com/p/gerrit/)\n#\n# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nCHANGE_ID_AFTER=\"Bug|Issue|Story\"\nMSG=\"$1\"\n\n# Check for, and add if missing, a unique Change-Id\n#\nadd_ChangeId() {\n        clean_message=`sed -e '\n                /^diff --git a\\/.*/{\n                        s///\n                        q\n                }\n                /^Signed-off-by:/d\n                /^#/d\n        ' \"$MSG\" | git stripspace`\n        if test -z \"$clean_message\"\n        then\n                return\n        fi\n\n        # Does Change-Id: already exist? if so, exit (no change).\n        if grep -i '^Change-Id:' \"$MSG\" >/dev/null\n        then\n                return\n        fi\n\n        id=`_gen_ChangeId`\n        T=\"$MSG.tmp.$$\"\n        AWK=awk\n        if [ -x /usr/xpg4/bin/awk ]; then\n                # Solaris AWK is just too broken\n                AWK=/usr/xpg4/bin/awk\n        fi\n\n        # How this works:\n        # - parse the commit message as (textLine+ blankLine*)*\n        # - assume textLine+ to be a footer until proven otherwise\n        # - exception: the first block is not footer (as it is the title)\n        # - read textLine+ into a variable\n        # - then count blankLines\n        # - once the next textLine appears, print textLine+ blankLine* as these\n        #   aren't footer\n        # - in END, the last textLine+ block is available for footer parsing\n        $AWK '\n        BEGIN {\n                # while we start with the assumption that textLine+\n                # is a footer, the first block is not.\n                isFooter = 0\n                footerComment = 0\n                blankLines = 0\n        }\n\n        # Skip lines starting with \"#\" without any spaces before it.\n        /^#/ { next }\n\n        # Skip the line starting with the diff command and everything after it,\n        # up to the end of the file, assuming it is only patch data.\n        # If more than one line before the diff was empty, strip all but one.\n        /^diff --git a/ {\n                blankLines = 0\n                while (getline) { }\n                next\n        }\n\n        # Count blank lines outside footer comments\n        /^$/ && (footerComment == 0) {\n                blankLines++\n                next\n        }\n\n        # Catch footer comment\n        /^\\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {\n                footerComment = 1\n        }\n\n        /]$/ && (footerComment == 1) {\n                footerComment = 2\n        }\n\n        # We have a non-blank line after blank lines. Handle this.\n        (blankLines > 0) {\n                print lines\n                for (i = 0; i < blankLines; i++) {\n                        print \"\"\n                }\n\n                lines = \"\"\n                blankLines = 0\n                isFooter = 1\n                footerComment = 0\n        }\n\n        # Detect that the current block is not the footer\n        (footerComment == 0) && (!/^\\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\\/\\//) {\n                isFooter = 0\n        }\n\n        {\n                # We need this information about the current last comment line\n                if (footerComment == 2) {\n                        footerComment = 0\n                }\n                if (lines != \"\") {\n                        lines = lines \"\\n\";\n                }\n                lines = lines $0\n        }\n\n        # Footer handling:\n        # If the last block is considered a footer, splice in the Change-Id at the\n        # right place.\n        # Look for the right place to inject Change-Id by considering\n        # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,\n        # then Change-Id, then everything else (eg. Signed-off-by:).\n        #\n        # Otherwise just print the last block, a new line and the Change-Id as a\n        # block of its own.\n        END {\n                unprinted = 1\n                if (isFooter == 0) {\n                        print lines \"\\n\"\n                        lines = \"\"\n                }\n                changeIdAfter = \"^(\" tolower(\"'\"$CHANGE_ID_AFTER\"'\") \"):\"\n                numlines = split(lines, footer, \"\\n\")\n                for (line = 1; line <= numlines; line++) {\n                        if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {\n                                unprinted = 0\n                                print \"Change-Id: I'\"$id\"'\"\n                        }\n                        print footer[line]\n                }\n                if (unprinted) {\n                        print \"Change-Id: I'\"$id\"'\"\n                }\n        }' \"$MSG\" > $T && mv $T \"$MSG\" || rm -f $T\n}\n_gen_ChangeIdInput() {\n        echo \"tree `git write-tree`\"\n        if parent=`git rev-parse \"HEAD^0\" 2>/dev/null`\n        then\n                echo \"parent $parent\"\n        fi\n        echo \"author `git var GIT_AUTHOR_IDENT`\"\n        echo \"committer `git var GIT_COMMITTER_IDENT`\"\n        echo\n        printf '%s' \"$clean_message\"\n}\n_gen_ChangeId() {\n        _gen_ChangeIdInput |\n        git hash-object -t commit --stdin\n}\n\n\nadd_ChangeId\n"
  },
  {
    "path": "libexec/index-tags",
    "content": "#!/bin/sh\n# Indexes all tags for this repository for easy navigation with Vim, storing the\n# file in <repo>/.git/tags. If you're using Fugitive.vim, you'll automatically\n# have access to the tags file; otherwise you'll have to modify your `tags`\n# option in your vimrc to search the generated file.\n\nset -e\n\ndir=\"`git rev-parse --git-dir`\"\n\ntrap \"rm -f $dir/tags.$$\" EXIT\nerr_file=$dir/ctags.err\nif ctags --tag-relative -Rf$dir/tags.$$ --exclude=.git \"$@\" 2>${err_file}; then\n  mv $dir/tags.$$ $dir/tags\n  [ -e ${err_file} ] && rm -f ${err_file}\nelse\n  # Ignore STDERR unless `ctags` returned a non-zero exit code\n  cat ${err_file}\nfi\n"
  },
  {
    "path": "overcommit.gemspec",
    "content": "# frozen_string_literal: true\n\nrequire_relative './lib/overcommit/constants'\nrequire_relative './lib/overcommit/version'\n\nGem::Specification.new do |s|\n  s.name                  = 'overcommit'\n  s.version               = Overcommit::VERSION\n  s.license               = 'MIT'\n  s.summary               = 'Git hook manager'\n  s.description           = 'Utility to install, configure, and extend Git hooks'\n  s.authors               = ['Shane da Silva']\n  s.email                 = ['shane@dasilva.io']\n  s.homepage              = Overcommit::REPO_URL\n  s.post_install_message  =\n    'Install hooks by running `overcommit --install` in your Git repository'\n\n  s.metadata = {\n    'changelog_uri' => 'https://github.com/sds/overcommit/blob/main/CHANGELOG.md'\n  }\n\n  s.require_paths         = %w[lib]\n\n  s.executables           = ['overcommit']\n\n  s.files                 = Dir['bin/**/*'] +\n                            Dir['config/*.yml'] +\n                            Dir['lib/**/*.rb'] +\n                            Dir['libexec/**/*'] +\n                            Dir['template-dir/**/*']\n\n  s.required_ruby_version = '>= 2.6'\n\n  s.add_dependency          'childprocess', '>= 0.6.3', '< 6'\n  s.add_dependency          'iniparse', '~> 1.4'\n  s.add_dependency          'rexml', '>= 3.3.9'\nend\n"
  },
  {
    "path": "spec/integration/committing_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'commiting' do\n  subject { shell(%w[git commit --allow-empty -m Test]) }\n\n  let(:config) { <<-YML }\n    CommitMsg:\n      ALL:\n        enabled: false\n    PreCommit:\n      ALL:\n        enabled: false\n      AuthorName:\n        enabled: true\n  YML\n\n  around do |example|\n    repo do\n      File.open('.overcommit.yml', 'w') { |f| f.write(config) }\n      `overcommit --install > #{File::NULL}`\n      example.run\n    end\n  end\n\n  context 'when a hook fails' do\n    before do\n      `git config --local user.name \"\"`\n    end\n\n    it 'exits with a non-zero status' do\n      subject.status.should_not == 0\n    end\n  end\n\n  context 'when no hooks fail on single author name' do\n    before do\n      `git config --local user.name \"John\"`\n    end\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n  end\n\n  context 'when no hooks fail' do\n    before do\n      `git config --local user.name \"John Doe\"`\n    end\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n  end\nend\n\ndescribe 'commiting to an empty repo' do\n  subject { shell(%w[git commit -m Test]) }\n\n  let(:config) { <<-YML }\n    CommitMsg:\n      ALL:\n        enabled: false\n    PreCommit:\n      ALL:\n        enabled: false\n      HardTabs:\n        enabled: true\n  YML\n\n  around do |example|\n    repo do\n      `overcommit --install > #{File::NULL}`\n      File.open('.overcommit.yml', 'w') { |f| f.write(config) }\n      File.open('test.txt', 'w') { |f| f.write(file_contents) }\n      `git add test.txt`\n      example.run\n    end\n  end\n\n  context 'when a hook fails' do\n    let(:file_contents) { \"\\t\\tFile with some hard tabs\" }\n\n    it 'exits with a non-zero status' do\n      subject.status.should_not == 0\n    end\n\n    it 'does not complain about missing HEAD' do\n      subject.stderr.should_not include 'HEAD'\n    end\n\n    it 'does not lose changes' do\n      File.open('test.txt').read.should == file_contents\n    end\n  end\n\n  context 'when no hooks fail' do\n    let(:file_contents) { 'File without hard tabs' }\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n\n    it 'does not complain about missing HEAD' do\n      subject.stderr.should_not include 'HEAD'\n    end\n\n    it 'does not lose changes' do\n      subject\n      File.open('test.txt').read.should == file_contents\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/configuration_signing_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'yaml'\n\ndescribe 'configuration file signing' do\n  let(:enable_verification) { true }\n  let(:new_verify_signatures) { verify_signatures }\n\n  let(:config) do\n    {\n      'verify_signatures' => verify_signatures,\n      'CommitMsg' => {\n        'ALL' => { 'enabled' => false },\n      },\n      'PreCommit' => {\n        'ALL' => { 'enabled' => false },\n      },\n    }\n  end\n\n  let(:new_config) do\n    config.dup.tap do |conf|\n      conf['verify_signatures'] = new_verify_signatures\n    end\n  end\n\n  subject { shell(%w[git commit --allow-empty -m Test]) }\n\n  around do |example|\n    repo do\n      `overcommit --install > #{File::NULL}`\n      echo(config.to_yaml, '.overcommit.yml')\n\n      `overcommit --sign` if configuration_signed\n      echo(new_config.to_yaml, '.overcommit.yml')\n\n      example.run\n    end\n  end\n\n  context 'when verify_signatures is true' do\n    let(:verify_signatures) { true }\n\n    context 'and the configuration has not been signed' do\n      let(:configuration_signed) { false }\n\n      it 'reports a signature error' do\n        subject.status.should_not == 0\n      end\n    end\n\n    context 'and the configuration has been signed' do\n      let(:configuration_signed) { true }\n\n      it 'does not report a signature error' do\n        subject.status.should == 0\n      end\n\n      context 'and verify_signatures was changed to false' do\n        let(:new_verify_signatures) { false }\n\n        it 'reports a signature error' do\n          subject.status.should_not == 0\n        end\n      end\n    end\n  end\n\n  context 'when verify_signatures is false' do\n    let(:verify_signatures) { false }\n\n    context 'and the configuration has not been signed' do\n      let(:configuration_signed) { false }\n\n      it 'reports a signature error' do\n        subject.status.should_not == 0\n      end\n    end\n\n    context 'and the configuration has been signed' do\n      let(:configuration_signed) { true }\n\n      it 'does not report a signature error' do\n        subject.status.should == 0\n      end\n\n      context 'and verify_signatures was changed to true' do\n        let(:new_verify_signatures) { true }\n\n        it 'reports a signature error' do\n          subject.status.should_not == 0\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/diff_flag_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'overcommit --diff' do\n  subject { shell(%w[overcommit --diff main]) }\n\n  context 'when using an existing pre-commit hook script' do\n    let(:script_name) { 'test-script' }\n    let(:script_contents) { \"#!/bin/bash\\nexit 0\" }\n    let(:script_path) { \".#{Overcommit::OS::SEPARATOR}#{script_name}\" }\n\n    let(:config) do\n      {\n        'PreCommit' => {\n          'MyHook' => {\n            'enabled' => true,\n            'required_executable' => script_path,\n          }\n        }\n      }\n    end\n\n    around do |example|\n      repo do\n        File.open('.overcommit.yml', 'w') { |f| f.puts(config.to_yaml) }\n        echo(script_contents, script_path)\n        `git add #{script_path}`\n        FileUtils.chmod(0o755, script_path)\n        example.run\n      end\n    end\n\n    it 'completes successfully without blocking' do\n      wait_until(timeout: 10) { subject } # Need to wait long time for JRuby startup\n      subject.status.should == 0\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/disable_overcommit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'disabling Overcommit' do\n  subject { shell(%w[git commit --allow-empty -m Test]) }\n\n  around do |example|\n    repo do\n      `overcommit --install > #{File::NULL}`\n      Overcommit::Utils.with_environment('OVERCOMMIT_DISABLE' => overcommit_disable) do\n        touch 'blah'\n        `git add blah`\n        example.run\n      end\n    end\n  end\n\n  context 'when the OVERCOMMIT_DISABLE environment variable is set' do\n    let(:overcommit_disable) { '1' }\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n\n    it 'does not run any hooks' do\n      subject.stdout.should_not be_empty\n      subject.stderr.should_not include 'Running pre-commit hooks'\n    end\n  end\n\n  context 'when the OVERCOMMIT_DISABLE environment variable is set to zero' do\n    let(:overcommit_disable) { '0' }\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n\n    it 'runs the hooks' do\n      subject.stderr.should include 'Running pre-commit hooks'\n    end\n  end\n\n  context 'when the OVERCOMMIT_DISABLE environment variable is unset' do\n    let(:overcommit_disable) { nil }\n\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n\n    it 'runs the hooks' do\n      subject.stderr.should include 'Running pre-commit hooks'\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/gemfile_option_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'specifying `gemfile` option in Overcommit configuration' do\n  context 'given a project that uses a Gemfile' do\n    let(:repo_root) { File.expand_path(File.join('..', '..'), File.dirname(__FILE__)) }\n    let(:fake_gem_path) { File.join('lib', 'my_fake_gem') }\n\n    # We point the overcommit gem back to this repo since we can't assume the gem\n    # has already been installed in a test environment\n    let(:gemfile) { normalize_indent(<<-RUBY) }\n      source 'https://rubygems.org'\n\n      gem 'overcommit', path: '#{repo_root}'\n      gem 'my_fake_gem', path: '#{fake_gem_path}'\n      gem 'ffi' if Gem.win_platform? # Necessary for test to pass on Windows\n    RUBY\n\n    let(:gemspec) { normalize_indent(<<-RUBY) }\n      Gem::Specification.new do |s|\n        s.name = 'my_fake_gem'\n        s.version = '1.0.0'\n        s.author = 'John Doe'\n        s.license = 'MIT'\n        s.homepage = 'https://example.com'\n        s.email = 'john.doe@example.com'\n        s.summary = 'A fake gem'\n        s.files = [File.join('lib', 'my_fake_gem.rb')]\n      end\n    RUBY\n\n    # Specify a hook that depends on an external gem to test Gemfile loading\n    let(:hook) { normalize_indent(<<-RUBY) }\n      module Overcommit::Hook::PreCommit\n        class FakeHook < Base\n          def run\n            require 'my_fake_gem'\n            :pass\n          end\n        end\n      end\n    RUBY\n\n    let(:config) { normalize_indent(<<-YAML) }\n      verify_signatures: false\n\n      CommitMsg:\n        ALL:\n          enabled: false\n\n      PreCommit:\n        ALL:\n          enabled: false\n        FakeHook:\n          enabled: true\n          requires_files: false\n    YAML\n\n    around do |example|\n      repo do\n        # Since RSpec is being run within a Bundler context we need to clear it\n        # in order to not taint the test\n        Bundler.with_unbundled_env do\n          FileUtils.mkdir_p(File.join(fake_gem_path, 'lib'))\n          echo(gemspec, File.join(fake_gem_path, 'my_fake_gem.gemspec'))\n          touch(File.join(fake_gem_path, 'lib', 'my_fake_gem.rb'))\n\n          echo(gemfile, '.overcommit_gems.rb')\n          `bundle install --gemfile=.overcommit_gems.rb`\n\n          echo(config, '.overcommit.yml')\n\n          # Set BUNDLE_GEMFILE so we load Overcommit from the current repo\n          ENV['BUNDLE_GEMFILE'] = '.overcommit_gems.rb'\n          `bundle exec overcommit --install > #{File::NULL}`\n          FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit'))\n          echo(hook, File.join('.git-hooks', 'pre_commit', 'fake_hook.rb'))\n\n          Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do\n            example.run\n          end\n        end\n      end\n    end\n\n    subject { shell(%w[git commit --allow-empty -m Test]) }\n\n    context 'when configuration specifies the gemfile' do\n      let(:config) { \"gemfile: .overcommit_gems.rb\\n\" + super() }\n\n      it 'runs the hook successfully' do\n        subject.status.should == 0\n      end\n    end\n\n    context 'when configuration does not specify the gemfile' do\n      it 'fails to run the hook' do\n        subject.status.should_not == 0\n      end\n    end\n  end\n\n  context 'given a project that does not use a Gemfile' do\n    let(:hook) { normalize_indent(<<-RUBY) }\n      module Overcommit::Hook::PreCommit\n        class NoInvalidGemfileHook < Base\n          def run\n            if (gemfile = ENV[\"BUNDLE_GEMFILE\"])\n              raise unless File.exist?(gemfile)\n            end\n\n            :pass\n          end\n        end\n      end\n    RUBY\n\n    let(:config) { normalize_indent(<<-YAML) }\n      verify_signatures: false\n\n      CommitMsg:\n        ALL:\n          enabled: false\n\n      PreCommit:\n        ALL:\n          enabled: false\n        NoInvalidGemfileHook:\n          enabled: true\n          requires_files: false\n    YAML\n\n    around do |example|\n      repo do\n        echo(config, '.overcommit.yml')\n\n        `overcommit --install > #{File::NULL}`\n        FileUtils.mkdir_p(File.join('.git-hooks', 'pre_commit'))\n        echo(hook, File.join('.git-hooks', 'pre_commit', 'no_invalid_gemfile_hook.rb'))\n\n        Overcommit::Utils.with_environment 'OVERCOMMIT_NO_VERIFY' => '1' do\n          example.run\n        end\n      end\n    end\n\n    subject { shell(%w[git commit --allow-empty -m Test]) }\n\n    context 'when configuration explicitly sets the gemfile to false' do\n      let(:config) { \"gemfile: false\\n\" + super() }\n\n      it 'runs the hook successfully' do\n        subject.status.should == 0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/hook_signing_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'yaml'\n\ndescribe 'hook signing' do\n  let(:enable_verification) { true }\n  let(:fake_hook_config) do\n    {\n      'enabled' => true,\n      'requires_files' => false,\n      'required_executable' => script_path,\n    }\n  end\n\n  let(:script_path) do\n    Overcommit::OS.windows? ? '.\\\\pre-commit.bat' : './pre-commit'\n  end\n\n  let(:hook_script) { normalize_indent(<<-BASH) }\n    echo Hello\n  BASH\n\n  let(:config) do\n    {\n      'verify_signatures' => verify_signatures,\n      'CommitMsg' => {\n        'ALL' => { 'enabled' => false },\n      },\n      'PreCommit' => {\n        'ALL' => { 'enabled' => false },\n        'FakeHook' => fake_hook_config,\n      },\n    }\n  end\n\n  subject { shell(%w[git commit --allow-empty -m Test]) }\n\n  context 'when a plugin hook configuration is changed' do\n    let(:new_fake_hook_config) { fake_hook_config.merge('some_option' => true) }\n\n    let(:new_config) do\n      config.dup.tap do |conf|\n        conf['PreCommit']['FakeHook'] = new_fake_hook_config\n      end\n    end\n\n    around do |example|\n      repo do\n        echo(config.to_yaml, '.overcommit.yml')\n        `overcommit --install > #{File::NULL}`\n\n        echo(hook_script, script_path)\n        FileUtils.chmod(0o755, script_path)\n        `git add #{script_path}`\n\n        `overcommit --sign`\n        `overcommit --sign pre-commit`\n        echo(new_config.to_yaml, '.overcommit.yml')\n\n        example.run\n      end\n    end\n\n    context 'and signatures are verified' do\n      let(:verify_signatures) { true }\n\n      it 'reports a signature error' do\n        subject.status.should_not == 0\n      end\n    end\n\n    context 'and signatures are not verified' do\n      let(:verify_signatures) { false }\n\n      it 'does not report a signature error' do\n        subject.status.should == 0\n      end\n    end\n  end\n\n  context 'and a plugin hook is changed' do\n    let(:new_hook_script) { normalize_indent(<<-BASH) }\n      echo This could potentially be malicious code\n    BASH\n\n    around do |example|\n      repo do\n        echo(config.to_yaml, '.overcommit.yml')\n        `overcommit --install > #{File::NULL}`\n\n        echo(hook_script, script_path)\n        FileUtils.chmod(0o755, script_path)\n        `git add #{script_path}`\n\n        `overcommit --sign`\n        `overcommit --sign pre-commit`\n        echo(new_hook_script, script_path)\n\n        example.run\n      end\n    end\n\n    context 'and signatures are verified' do\n      let(:verify_signatures) { true }\n\n      it 'reports a signature error' do\n        subject.status.should_not == 0\n      end\n    end\n\n    context 'and signatures are not verified' do\n      let(:verify_signatures) { false }\n\n      it 'does not report a signature error' do\n        subject.status.should == 0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/installing_overcommit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'installing Overcommit' do\n  let(:enable_verification) { true }\n\n  it 'signs the configuration file' do\n    repo do\n      `overcommit --install`\n      touch('some-file')\n      `git add some-file`\n      result = shell(%w[git commit --allow-empty -m Test])\n      result.status.should == 0\n    end\n  end\n\n  context 'when template directory points to the Overcommit template directory' do\n    around do |example|\n      repo(template_dir: Overcommit::Installer::TEMPLATE_DIRECTORY) do\n        example.run\n      end\n    end\n\n    it 'automatically installs Overcommit hooks for new repositories' do\n      Overcommit::Utils.supported_hook_types.each do |hook_type|\n        hook_file = File.join('.git', 'hooks', hook_type)\n        File.read(hook_file).should include 'OVERCOMMIT'\n      end\n    end\n\n    context 'and Overcommit is manually installed' do\n      before do\n        `overcommit --install`\n      end\n\n      it 'leaves the hooks intact' do\n        Overcommit::Utils.supported_hook_types.each do |hook_type|\n          hook_file = File.join('.git', 'hooks', hook_type)\n          File.read(hook_file).should include 'OVERCOMMIT'\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/parallelize_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'timeout'\n\ndescribe 'running a hook with parallelism disabled' do\n  subject { shell(%w[git commit --allow-empty -m Test]) }\n\n  let(:config) { <<-YML }\n    concurrency: 20\n    CommitMsg:\n      TrailingPeriod:\n        enabled: true\n        parallelize: false\n        command: ['ruby', '-e', 'sleep 1']\n      TextWidth:\n        enabled: true\n        parallelize: true\n        processors: 1\n  YML\n\n  around do |example|\n    repo do\n      File.open('.overcommit.yml', 'w') { |f| f.write(config) }\n      `overcommit --install > #{File::NULL}`\n      example.run\n    end\n  end\n\n  # Test fails on Ruby 3.0 on Windows but nothing else. Would glady accept a pull\n  # request that resolves.\n  unless Overcommit::OS.windows? &&\n    Overcommit::Utils::Version.new(RUBY_VERSION) >= '3' &&\n    Overcommit::Utils::Version.new(RUBY_VERSION) < '3.1'\n    it 'does not hang' do\n      result = Timeout.timeout(5) { subject }\n      result.stderr.should_not include 'No live threads left. Deadlock?'\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/protected_branches_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::ProtectedBranches,\n         if: Overcommit::GIT_VERSION >= '2.0' do\n  let(:flags) { '' }\n  let(:pushed_ref) { remote_ref }\n  subject do\n    shell(\"git push #{flags} origin #{pushed_ref}:#{remote_ref}\".split)\n  end\n\n  let(:config) { <<-YML }\n    CommitMsg:\n      ALL:\n        enabled: false\n    PreCommit:\n      ALL:\n        enabled: false\n    PrePush:\n      ALL:\n        enabled: false\n      ProtectedBranches:\n        enabled: true\n        branches:\n          - protected\n          - protected_for_destructive_only:\n            destructive_only: true\n  YML\n\n  around do |example|\n    remote_repo = repo do\n      `git checkout -b protected > #{File::NULL} 2>&1`\n      `git commit --allow-empty -m \"Remote commit\"`\n      `git checkout -b unprotected > #{File::NULL} 2>&1`\n      `git checkout -b dummy > #{File::NULL} 2>&1`\n    end\n    repo do\n      File.open('.overcommit.yml', 'w') { |f| f.write(config) }\n      `git remote add origin file://#{remote_repo}`\n      `git checkout -b #{remote_ref} > #{File::NULL} 2>&1`\n      `git commit --allow-empty -m \"Local commit\"`\n      example.run\n    end\n  end\n\n  shared_context 'deleting' do\n    let(:pushed_ref) { '' }\n  end\n\n  shared_context 'force-pushing' do\n    let(:flags) { '--force' }\n  end\n\n  shared_context 'remote exists locally' do\n    before { `git fetch origin #{remote_ref} > #{File::NULL} 2>&1` }\n  end\n\n  shared_context 'local branch up-to-date' do\n    before { `git rebase --keep-empty origin/#{remote_ref} > #{File::NULL} 2>&1` }\n  end\n\n  shared_context 'ProtectedBranches enabled' do\n    before { `overcommit --install > #{File::NULL}` }\n  end\n\n  shared_examples 'push succeeds' do\n    it 'exits successfully' do\n      subject.status.should == 0\n    end\n  end\n\n  shared_examples 'push fails' do\n    it 'exits with a non-zero status' do\n      subject.status.should_not == 0\n    end\n  end\n\n  shared_examples 'push succeeds when remote exists locally' do\n    context 'when remote exists locally' do\n      include_context 'remote exists locally'\n\n      context 'when up-to-date with remote' do\n        include_context 'local branch up-to-date'\n        include_examples 'push succeeds'\n      end\n\n      context 'when not up-to-date with remote' do\n        include_examples 'push succeeds'\n      end\n    end\n\n    context 'when remote does not exist locally' do\n      include_examples 'push fails'\n    end\n  end\n\n  shared_examples 'push succeeds when up-to-date with remote' do\n    context 'when remote exists locally' do\n      include_context 'remote exists locally'\n\n      context 'when up-to-date with remote' do\n        include_context 'local branch up-to-date'\n        include_examples 'push succeeds'\n      end\n\n      context 'when not up-to-date with remote' do\n        include_examples 'push fails'\n      end\n    end\n\n    context 'when remote does not exist locally' do\n      include_examples 'push fails'\n    end\n  end\n\n  shared_examples 'push always fails' do\n    context 'when remote exists locally' do\n      include_context 'remote exists locally'\n\n      context 'when up-to-date with remote' do\n        include_context 'local branch up-to-date'\n        include_examples 'push fails'\n      end\n\n      context 'when not up-to-date with remote' do\n        include_examples 'push fails'\n      end\n    end\n\n    context 'when remote does not exist locally' do\n      include_examples 'push fails'\n    end\n  end\n\n  shared_examples 'push always succeeds' do\n    context 'when remote exists locally' do\n      include_context 'remote exists locally'\n\n      context 'when up-to-date with remote' do\n        include_context 'local branch up-to-date'\n        include_examples 'push succeeds'\n      end\n\n      context 'when not up-to-date with remote' do\n        include_examples 'push succeeds'\n      end\n    end\n\n    context 'when remote does not exist locally' do\n      include_examples 'push succeeds'\n    end\n  end\n\n  context 'when pushing to a protected branch' do\n    let(:remote_ref) { 'protected' }\n\n    context 'when force-pushing' do\n      include_context 'force-pushing'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push succeeds when up-to-date with remote'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push always succeeds'\n      end\n    end\n\n    context 'when deleting' do\n      include_context 'deleting'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push always fails'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push always succeeds'\n      end\n    end\n\n    context 'when not deleting or force-pushing' do\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push succeeds when up-to-date with remote'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push succeeds when up-to-date with remote'\n      end\n    end\n  end\n\n  context 'when pushing to an unprotected branch' do\n    let(:remote_ref) { 'unprotected' }\n\n    context 'when force-pushing' do\n      include_context 'force-pushing'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push always succeeds'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push always succeeds'\n      end\n    end\n\n    context 'when deleting' do\n      include_context 'deleting'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push always succeeds'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push always succeeds'\n      end\n    end\n\n    context 'when not deleting or force-pushing' do\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push succeeds when up-to-date with remote'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push succeeds when up-to-date with remote'\n      end\n    end\n  end\n\n  context 'when pushing to a nonexistent branch' do\n    let(:remote_ref) { 'new-branch' }\n\n    context 'when force-pushing' do\n      include_context 'force-pushing'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push succeeds'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push succeeds'\n      end\n    end\n\n    context 'when deleting' do\n      include_context 'deleting'\n\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push fails'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push fails'\n      end\n    end\n\n    context 'when not deleting or force-pushing' do\n      context 'with ProtectedBranches enabled' do\n        include_context 'ProtectedBranches enabled'\n        include_examples 'push succeeds'\n      end\n\n      context 'with ProtectedBranches disabled' do\n        include_examples 'push succeeds'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/resolving_cherry_pick_conflict_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'resolving cherry-pick conflicts' do\n  subject { shell(%w[git commit -m Test -i some-file]) }\n\n  let(:config) { <<-YML }\n    PreCommit:\n      TrailingWhitespace:\n        enabled: true\n  YML\n\n  around do |example|\n    repo do\n      File.open('.overcommit.yml', 'w') { |f| f.write(config) }\n      `git add .overcommit.yml`\n      `git commit -m \"Add Overcommit config\"`\n      echo('Master', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add some-file\"`\n      `git checkout -q -b branch1`\n      echo('Branch 1 Addition', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add Branch 1 addition\"`\n      `git checkout -q master`\n      `git checkout -q -b branch2`\n      echo('Branch 2 Addition', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add Branch 2 addition\"`\n      `git checkout -q master`\n      `git cherry-pick branch1 > #{File::NULL} 2>&1`\n      `overcommit --install > #{File::NULL}`\n      `git cherry-pick branch2 > #{File::NULL} 2>&1` # Results in cherry-pick conflict\n      echo('Conflicts Resolved ', 'some-file') # Fail trailing whitespace hook\n      `git add some-file`\n      example.run\n    end\n  end\n\n  it 'exits with a non-zero status' do\n    skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']\n    subject.status.should_not == 0\n  end\n\n  it 'does not remove the CHERRY_PICK_HEAD file' do\n    skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']\n    subject\n    Dir['.git/*'].should include '.git/CHERRY_PICK_HEAD'\n  end\n\n  it 'keeps the commit message from the cherry-picked commit' do\n    skip 'Skipping flakey test on AppVeyor Windows builds' if ENV['APPVEYOR']\n    subject\n    File.read(File.join('.git', 'MERGE_MSG')).should include 'Add Branch 2 addition'\n  end\nend\n"
  },
  {
    "path": "spec/integration/resolving_merge_conflict_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'resolving merge conflicts' do\n  subject { shell(%w[git commit -m Test -i some-file]) }\n\n  around do |example|\n    repo do\n      echo('Master', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add some-file\"`\n      `git checkout -q -b branch1`\n      echo('Branch 1 Addition', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add Branch 1 addition\"`\n      `git checkout -q master`\n      `git checkout -q -b branch2`\n      echo('Branch 2 Addition', 'some-file')\n      `git add some-file`\n      `git commit -m \"Add Branch 2 addition\"`\n      `git checkout -q master`\n      `git merge branch1`\n      `git merge branch2` # Results in merge conflict\n      `overcommit --install > #{File::NULL}`\n      echo('Conflicts Resolved', 'some-file')\n      `git add some-file`\n      example.run\n    end\n  end\n\n  it 'exits successfully' do\n    subject.status.should == 0\n  end\n\n  it 'does not display an error about MERGE_HEAD missing' do\n    subject.stderr.should_not include 'MERGE_HEAD'\n  end\nend\n"
  },
  {
    "path": "spec/integration/run_flag_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'overcommit --run' do\n  subject { shell(%w[overcommit --run]) }\n\n  context 'when using an existing pre-commit hook script' do\n    if Overcommit::OS.windows?\n      let(:script_name) { 'test-script.bat' }\n      let(:script_contents) { 'exit 0' }\n    else\n      let(:script_name) { 'test-script' }\n      let(:script_contents) { \"#!/bin/bash\\nexit 0\" }\n    end\n    let(:script_path) { \".#{Overcommit::OS::SEPARATOR}#{script_name}\" }\n\n    let(:config) do\n      {\n        'PreCommit' => {\n          'MyHook' => {\n            'enabled' => true,\n            'required_executable' => script_path,\n          }\n        }\n      }\n    end\n\n    around do |example|\n      repo do\n        File.open('.overcommit.yml', 'w') { |f| f.puts(config.to_yaml) }\n        echo(script_contents, script_path)\n        `git add #{script_path}`\n        FileUtils.chmod(0o755, script_path)\n        example.run\n      end\n    end\n\n    it 'completes successfully without blocking' do\n      wait_until(timeout: 10) { subject } # Need to wait long time for JRuby startup\n      subject.status.should == 0\n    end\n  end\nend\n"
  },
  {
    "path": "spec/integration/template_dir_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'fileutils'\n\ndescribe 'template directory' do\n  let(:template_dir) { File.join(Overcommit::HOME, 'template-dir') }\n  let(:hooks_dir) { File.join(template_dir, 'hooks') }\n\n  it 'contains a hooks directory' do\n    File.directory?(hooks_dir).should == true\n  end\n\n  describe 'the hooks directory' do\n    it 'contains the master hook as an actual file with content' do\n      master_hook = File.join(hooks_dir, 'overcommit-hook')\n      File.exist?(master_hook).should == true\n      File.size?(master_hook).should > 0\n      Overcommit::Utils::FileUtils.symlink?(master_hook).should == false\n    end\n\n    it 'contains all other hooks as copies of the master hook' do\n      Overcommit::Utils.supported_hook_types.each do |hook_type|\n        FileUtils.compare_file(File.join(hooks_dir, hook_type),\n                               File.join(hooks_dir, 'overcommit-hook')).should == true\n      end\n    end\n\n    it 'contains no symlinks' do\n      Overcommit::Utils.supported_hook_types.each do |hook_type|\n        Overcommit::Utils::FileUtils.symlink?(File.join(hooks_dir, hook_type)).should == false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/cli_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/cli'\nrequire 'overcommit/hook_context/diff'\nrequire 'overcommit/hook_context/run_all'\n\ndescribe Overcommit::CLI do\n  describe '#run' do\n    let(:logger) { Overcommit::Logger.silent }\n    let(:input) { double('input') }\n    let(:cli) { described_class.new(arguments, input, logger) }\n    subject { cli.run }\n\n    before do\n      Overcommit::Utils.stub(:repo_root).and_return('current-dir')\n    end\n\n    context 'with no arguments' do\n      let(:arguments) { [] }\n\n      it 'attempts to install in the current directory' do\n        Overcommit::Installer.any_instance.\n                              should_receive(:run).\n                              with('current-dir',\n                                   hash_including(action: :install))\n        subject\n      end\n    end\n\n    context 'with the --list-hooks option specified' do\n      let(:arguments) { ['--list-hooks'] }\n\n      let(:contexts) do\n        Overcommit::ConfigurationLoader.new(logger).load_repo_config.all_hook_configs.keys\n      end\n\n      before { cli.stub(:halt) }\n\n      it 'prints the installed hooks' do\n        logger.should_receive(:log).at_least(contexts.count)\n        subject\n      end\n    end\n\n    context 'with the uninstall switch specified' do\n      let(:arguments) { ['--uninstall'] }\n\n      it 'uninstalls hooks from the current directory' do\n        Overcommit::Installer.any_instance.\n                              should_receive(:run).\n                              with('current-dir',\n                                   hash_including(action: :uninstall))\n        subject\n      end\n\n      context 'and an explicit target' do\n        let(:arguments) { super() + ['target-dir'] }\n\n        it 'uninstalls hooks from the target directory' do\n          Overcommit::Installer.any_instance.\n                                should_receive(:run).\n                                with('target-dir',\n                                     hash_including(action: :uninstall))\n          subject\n        end\n      end\n    end\n\n    context 'with the install switch specified' do\n      let(:arguments) { ['--install'] }\n\n      it 'installs hooks into the current directory' do\n        Overcommit::Installer.any_instance.\n                              should_receive(:run).\n                              with('current-dir',\n                                   hash_including(action: :install))\n        subject\n      end\n\n      context 'and an explicit target' do\n        let(:arguments) { super() + ['target-dir'] }\n\n        it 'installs hooks from the target directory' do\n          Overcommit::Installer.any_instance.\n                                should_receive(:run).\n                                with('target-dir',\n                                     hash_including(action: :install))\n          subject\n        end\n      end\n    end\n\n    context 'with the template directory switch specified' do\n      let(:arguments) { ['--template-dir'] }\n\n      before do\n        cli.stub(:halt)\n      end\n\n      it 'prints the location of the template directory' do\n        capture_stdout { subject }.chomp.should end_with 'template-dir'\n      end\n    end\n\n    context 'with the run switch specified' do\n      let(:arguments) { ['--run'] }\n      let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n\n      before do\n        cli.stub(:halt)\n      end\n\n      it 'creates a HookRunner with the run-all context' do\n        Overcommit::HookRunner.should_receive(:new).\n                               with(config,\n                                    logger,\n                                    instance_of(Overcommit::HookContext::RunAll),\n                                    instance_of(Overcommit::Printer)).\n                               and_call_original\n        subject\n      end\n\n      it 'runs the HookRunner' do\n        Overcommit::HookRunner.any_instance.should_receive(:run)\n        subject\n      end\n    end\n\n    context 'with the diff switch specified' do\n      let(:arguments) { ['--diff=some-ref'] }\n      let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n\n      before do\n        cli.stub(:halt)\n        Overcommit::HookRunner.any_instance.stub(:run)\n      end\n\n      it 'creates a HookRunner with the diff context' do\n        Overcommit::HookRunner.should_receive(:new).\n          with(config,\n               logger,\n               instance_of(Overcommit::HookContext::Diff),\n               instance_of(Overcommit::Printer)).\n          and_call_original\n        subject\n      end\n\n      it 'runs the HookRunner' do\n        Overcommit::HookRunner.any_instance.should_receive(:run)\n        subject\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/command_splitter_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::CommandSplitter do\n  describe '.execute' do\n    let(:args_prefix) { %w[cmd] }\n    let(:max_command_length) { 10 }\n    let(:options) { { args: splittable_args } }\n\n    subject { described_class.execute(args_prefix, options) }\n\n    before do\n      described_class.stub(:max_command_length).and_return(max_command_length)\n\n      Overcommit::Subprocess.stub(:spawn).\n        and_return(Overcommit::Subprocess::Result.new(0, 'output', 'error'))\n    end\n\n    context 'with no splittable arguments' do\n      let(:splittable_args) { [] }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidCommandArgs\n      end\n    end\n\n    context 'with splittable arguments under the limit' do\n      let(:splittable_args) { %w[1 2 3 4 5 6 7] }\n\n      it 'executes one command' do\n        Overcommit::Subprocess.should_receive(:spawn).once\n        subject\n      end\n    end\n\n    context 'with splittable arguments just over the limit' do\n      let(:splittable_args) { %w[1 2 3 4 5 6 7 8] }\n\n      it 'executes two commands with the appropriately split arguments' do\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 1 2 3 4 5 6 7],\n                                                           hash_excluding(:args))\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 8],\n                                                           hash_excluding(:args))\n        subject\n      end\n\n      context 'when both commands return successfully' do\n        it 'returns a successful result' do\n          subject.should be_success\n          subject.status.should == 0\n        end\n\n        it 'returns concatenated output' do\n          subject.stdout.should == 'output' * 2\n          subject.stderr.should == 'error' * 2\n        end\n      end\n\n      context 'when one command fails' do\n        before do\n          Overcommit::Subprocess.stub(:spawn).\n            with(%w[cmd 8], anything).\n            and_return(Overcommit::Subprocess::Result.new(2, 'whoa', 'bad error'))\n        end\n\n        it 'returns an unsuccessful result' do\n          subject.should_not be_success\n          subject.status.should == 1\n        end\n\n        it 'returns concatenated output' do\n          subject.stdout.should == 'outputwhoa'\n          subject.stderr.should == 'errorbad error'\n        end\n      end\n    end\n\n    context 'with splittable arguments well over the limit' do\n      let(:splittable_args) { Array.new(15) { |i| (i + 1).to_s } }\n\n      it 'executes multiple commands with the appropriately split arguments' do\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 1 2 3 4 5 6 7],\n                                                           hash_excluding(:args))\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 8 9 10 11],\n                                                           hash_excluding(:args))\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 12 13 14],\n                                                           hash_excluding(:args))\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cmd 15],\n                                                           hash_excluding(:args))\n        subject\n      end\n    end\n\n    context 'with a splittable argument that on its own exceeds the limit' do\n      let(:splittable_args) { %w[1 2 ohmylookareallylongargument] }\n\n      it 'executes no commands and raises an exception' do\n        Overcommit::Subprocess.should_not_receive(:spawn)\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidCommandArgs\n      end\n    end\n\n    context 'with a command prefix that exceeds the limit' do\n      let(:args_prefix) { %w[reallylong] }\n      let(:splittable_args) { %w[1 2 3] }\n\n      it 'executes no commands and raises an exception' do\n        Overcommit::Subprocess.should_not_receive(:spawn)\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidCommandArgs\n      end\n    end\n\n    context 'with a standard input stream specified' do\n      let(:max_command_length) { 5 }\n      let(:args_prefix) { %w[cat] }\n      let(:options) { { args: %w[- - -], input: 'Hello' } }\n\n      it 'passes the same standard input to each command' do\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cat - -],\n                                                           hash_including(input: 'Hello'))\n        Overcommit::Subprocess.should_receive(:spawn).with(%w[cat -],\n                                                           hash_including(input: 'Hello'))\n        subject\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/configuration_loader_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::ConfigurationLoader do\n  let(:output) { StringIO.new }\n  let(:logger) { Overcommit::Logger.new(output) }\n\n  describe '#load_repo_config' do\n    subject { described_class.new(logger).load_repo_config }\n\n    context 'when repo does not contain a configuration file' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it 'returns the default configuration' do\n        subject.should == described_class.default_configuration\n      end\n    end\n\n    context 'when repo contains a configuration file' do\n      let(:config_contents) { <<-CFG }\n        plugin_directory: 'some-directory'\n      CFG\n\n      around do |example|\n        repo do\n          File.open('.overcommit.yml', 'w') { |f| f.write(config_contents) }\n          example.run\n        end\n      end\n\n      it 'loads the file' do\n        Overcommit::ConfigurationLoader.any_instance.\n          should_receive(:load_file).\n          with(File.expand_path('.overcommit.yml'))\n        subject\n      end\n\n      it 'merges the loaded file with the default configuration' do\n        subject.plugin_directory.should == File.expand_path('some-directory')\n      end\n\n      context 'and the configuration file contains a hook with no `enabled` option' do\n        let(:config_contents) { <<-CFG }\n          PreCommit:\n            ScssLint:\n              command: ['bundle', 'exec', 'scss-lint']\n        CFG\n\n        it 'displays a warning' do\n          subject\n          output.string.should =~ /PreCommit::ScssLint.*not.*enabled/i\n        end\n      end\n    end\n\n    context 'when repo only contains a repo level configuration file' do\n      let(:config_contents) { <<-CFG }\n        PreCommit:\n          Rubocop:\n            enabled: true\n      CFG\n\n      around do |example|\n        repo do\n          File.open('.overcommit.yml', 'w') { |f| f.write(config_contents) }\n          example.run\n        end\n      end\n\n      it 'includes default settings' do\n        subject\n        subject.for_hook('CapitalizedSubject', 'CommitMsg').should include('enabled' => true)\n      end\n\n      it 'includes .overwrite.yml configs' do\n        subject\n        subject.for_hook('Rubocop', 'PreCommit').should include('enabled' => true)\n      end\n    end\n\n    context 'when repo also contains a local configuration file' do\n      let(:local_config_contents) { <<-CFG }\n        plugin_directory: 'some-different-directory'\n      CFG\n\n      around do |example|\n        repo do\n          File.open('.overcommit.yml', 'w') { |f| f.write(config_contents) }\n          File.open('.local-overcommit.yml', 'w') { |f| f.write(local_config_contents) }\n          example.run\n        end\n      end\n\n      let(:config_contents) { <<-CFG }\n        PreCommit:\n          ScssLint:\n            enabled: true\n      CFG\n\n      let(:local_config_contents) { <<-CFG }\n        PreCommit:\n          Rubocop:\n            enabled: true\n      CFG\n\n      it 'includes default settings' do\n        subject\n        subject.for_hook('CapitalizedSubject', 'CommitMsg').should include('enabled' => true)\n      end\n\n      it 'includes .overwrite.yml configs' do\n        subject\n        subject.for_hook('ScssLint', 'PreCommit').should include('enabled' => true)\n      end\n\n      it 'includes .local-overwrite.yml configs' do\n        subject\n        subject.for_hook('Rubocop', 'PreCommit').should include('enabled' => true)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/configuration_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Configuration do\n  let(:hash) { {} }\n  let(:config) { described_class.new(hash) }\n\n  describe '#new' do\n    let(:internal_hash) { config.instance_variable_get(:@hash) }\n    subject { config }\n\n    context 'when no configuration exists for a hook type' do\n      it 'creates sections for those hook types' do\n        internal_hash.should have_key 'PreCommit'\n      end\n\n      it 'creates the special ALL section for the hook type' do\n        internal_hash['PreCommit'].should have_key 'ALL'\n      end\n    end\n\n    context 'when keys with empty values exist' do\n      let(:hash) do\n        {\n          'PreCommit' => {\n            'SomeHook' => nil\n          },\n        }\n      end\n\n      it 'converts the values to empty hashes' do\n        internal_hash['PreCommit']['SomeHook'].should == {}\n      end\n    end\n  end\n\n  describe '#plugin_directory' do\n    let(:hash) { { 'plugin_directory' => 'some-directory' } }\n    subject { config.plugin_directory }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    it { should == File.expand_path('some-directory') }\n  end\n\n  describe '#enabled_builtin_hooks' do\n    let(:hash) do\n      {\n        'PreCommit' => {\n          'AuthorName' => nil,\n          'AuthorEmail' => {\n            'enabled' => false\n          },\n        }\n      }\n    end\n\n    let(:context) { double('context') }\n    subject { config.enabled_builtin_hooks(context) }\n\n    before do\n      context.stub(hook_class_name: 'PreCommit',\n                   hook_type_name: 'pre_commit')\n    end\n\n    it 'excludes hooks that are not explicitly enabled' do\n      subject.should_not include 'AuthorName'\n    end\n  end\n\n  describe '#for_hook' do\n    let(:hash) do\n      {\n        'PreCommit' => {\n          'ALL' => {\n            'required' => false,\n          },\n          'SomeHook' => {\n            'enabled' => true,\n            'quiet' => false,\n          }\n        }\n      }\n    end\n\n    subject { config.for_hook('SomeHook', 'PreCommit') }\n\n    it 'returns the subset of the config for the specified hook' do\n      subject['enabled'].should == true\n      subject['quiet'].should == false\n    end\n\n    it 'merges the the hook config with the ALL section' do\n      subject['required'].should == false\n    end\n  end\n\n  describe '#merge' do\n    let(:parent_config) { described_class.new(parent) }\n    let(:child_config) { described_class.new(child) }\n    subject { parent_config.merge(child_config) }\n\n    context 'when parent and child are empty' do\n      let(:parent) { {} }\n      let(:child) { {} }\n\n      it 'returns a config equivalent to both' do\n        subject.should == parent_config\n        subject.should == child_config\n      end\n    end\n\n    context 'when parent and child are the same' do\n      let(:parent) { child }\n\n      let(:child) do\n        {\n          'plugin_directory' => 'some-directory',\n          'pre-commit' => {\n            'SomeHook' => {\n              'enabled' => false,\n            }\n          },\n        }\n      end\n\n      it 'returns a config equivalent to both' do\n        subject.should == parent_config\n        subject.should == child_config\n      end\n    end\n\n    context 'when parent item contains a hash' do\n      let(:parent) { { 'PreCommit' => { 'SomeHook' => { 'some-value' => 1 } } } }\n\n      context 'and child item contains a different hash under the same key' do\n        let(:child) { { 'PreCommit' => { 'SomeOtherHook' => { 'something' => 2 } } } }\n\n        it 'merges the hashes together' do\n          subject.for_hook('SomeHook', 'PreCommit').should include('some-value' => 1)\n          subject.for_hook('SomeOtherHook', 'PreCommit').should include('something' => 2)\n        end\n      end\n\n      context 'and child item contains a hash under a different key' do\n        let(:child) { { 'CommitMsg' => { 'SomeHook' => { 'some-value' => 2 } } } }\n\n        it 'appends the item to the parent array' do\n          subject.for_hook('SomeHook', 'PreCommit').should include('some-value' => 1)\n          subject.for_hook('SomeHook', 'CommitMsg').should include('some-value' => 2)\n        end\n      end\n\n      context 'and child item contains a hash under the ALL key' do\n        let(:child) do\n          {\n            'PreCommit' => {\n              'ALL' => { 'some-value' => 2 },\n              'SomeOtherHook' => { 'some-value' => 3 },\n            },\n          }\n        end\n\n        it 'overrides the value in the parent item' do\n          subject.for_hook('SomeHook', 'PreCommit').should include('some-value' => 2)\n        end\n\n        it 'does not override the value in other child items' do\n          subject.for_hook('SomeOtherHook', 'PreCommit').should include('some-value' => 3)\n        end\n\n        context 'and the parent contains a hash under the ALL key' do\n          let(:parent) do\n            super().tap do |hash|\n              hash['PreCommit']['ALL'] = { 'some-value' => 1 }\n            end\n          end\n\n          it 'overrides the ALL value in the parent item' do\n            subject.for_hook('SomeHook', 'PreCommit').should include('some-value' => 2)\n          end\n\n          it 'does not override the value in other child items' do\n            subject.for_hook('SomeOtherHook', 'PreCommit').should include('some-value' => 3)\n          end\n        end\n      end\n    end\n\n    context 'when parent item contains an array' do\n      let(:parent) { { 'PreCommit' => { 'SomeHook' => { 'list' => [1, 2, 3] } } } }\n\n      context 'and child item contains an array' do\n        let(:child) { { 'PreCommit' => { 'SomeHook' => { 'list' => [4, 5] } } } }\n\n        it 'overrides the value in the parent item' do\n          subject.for_hook('SomeHook', 'PreCommit')['list'].should == [4, 5]\n        end\n      end\n\n      context 'and child item contains a single item' do\n        let(:child) { { 'PreCommit' => { 'SomeHook' => { 'list' => 4 } } } }\n\n        it 'overrides the value in the parent item' do\n          subject.for_hook('SomeHook', 'PreCommit')['list'].should == 4\n        end\n      end\n    end\n  end\n\n  describe '#apply_environment!' do\n    let(:hash) { {} }\n    let(:config) { described_class.new(hash) }\n    let!(:old_config) { described_class.new(hash.dup) }\n    let(:context) { double('context') }\n    subject { config }\n\n    before do\n      context.stub(:hook_type_name).and_return('pre_commit')\n      context.stub(:hook_class_name).and_return('PreCommit')\n      config.apply_environment!(context, env)\n    end\n\n    context 'when no hooks are requested to be skipped' do\n      let(:env) { {} }\n\n      it 'does nothing to the configuration' do\n        subject.should == old_config\n      end\n    end\n\n    context 'when a non-existent hook is requested to be skipped' do\n      let(:env) { { 'SKIP' => 'SomeMadeUpHook' } }\n\n      it 'does nothing to the configuration' do\n        subject.should == old_config\n      end\n    end\n\n    context 'when an existing hook is requested to be skipped' do\n      let(:env) { { 'SKIP' => 'AuthorName' } }\n\n      it 'sets the skip option of the hook to true' do\n        subject.for_hook('AuthorName', 'PreCommit')['skip'].should == true\n      end\n\n      context 'and the hook is spelt with underscores' do\n        let(:env) { { 'SKIP' => 'author_name' } }\n\n        it 'sets the skip option of the hook to true' do\n          subject.for_hook('AuthorName', 'PreCommit')['skip'].should == true\n        end\n      end\n\n      context 'and the hook is spelt with hyphens' do\n        let(:env) { { 'SKIP' => 'author-name' } }\n\n        it 'sets the skip option of the hook to true' do\n          subject.for_hook('AuthorName', 'PreCommit')['skip'].should == true\n        end\n      end\n    end\n\n    context 'when the word \"all\" is included in the skip list' do\n      let(:env) { { 'SKIP' => 'all' } }\n\n      it 'sets the skip option of the ALL section to true' do\n        subject.for_hook('ALL', 'PreCommit')['skip'].should == true\n      end\n\n      context 'and \"all\" is capitalized' do\n        let(:env) { { 'SKIP' => 'ALL' } }\n\n        it 'sets the skip option of the special ALL config to true' do\n          subject.for_hook('ALL', 'PreCommit')['skip'].should == true\n        end\n      end\n    end\n\n    context 'when hooks are filtered using the ONLY environment variable' do\n      let(:env) { { 'ONLY' => 'AuthorName' } }\n\n      it 'sets the skip option of the ALL section to true' do\n        subject.for_hook('ALL', 'PreCommit')['skip'].should == true\n      end\n\n      it 'sets the skip option of the filtered hook to false' do\n        subject.for_hook('AuthorName', 'PreCommit')['skip'].should == false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/configuration_validator_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::ConfigurationValidator do\n  let(:output) { StringIO.new }\n  let(:logger) { Overcommit::Logger.new(output) }\n  let(:options) { { logger: logger } }\n  let(:config) { Overcommit::Configuration.new(config_hash, validate: false) }\n\n  subject { described_class.new.validate(config, config_hash, options) }\n\n  context 'when hook has an invalid name' do\n    let(:config_hash) do\n      {\n        'PreCommit' => {\n          'My_Hook' => {\n            'enabled' => false,\n          },\n        },\n      }\n    end\n\n    it 'raises an error' do\n      expect { subject }.to raise_error Overcommit::Exceptions::ConfigurationError\n    end\n  end\n\n  context 'when hook has `env` set' do\n    let(:config_hash) do\n      {\n        'PreCommit' => {\n          'MyHook' => {\n            'enabled' => true,\n            'env' => env,\n          },\n        },\n      }\n    end\n\n    context 'and it is a single string' do\n      let(:env) { 'OVERCOMMIT_ENV_VAR=1' }\n\n      it 'raises an error and mentions `env` must be a hash' do\n        expect { subject }.to raise_error Overcommit::Exceptions::ConfigurationError\n        output.string.should =~ /must be a hash/i\n      end\n    end\n\n    context 'and it is a hash with string values' do\n      let(:env) { { 'OVERCOMMIT_ENV_VAR' => '1', 'OVERCOMMIT_ENV_VAR_2' => '2' } }\n\n      it 'is valid' do\n        expect { subject }.not_to raise_error\n      end\n    end\n\n    context 'and it is a hash with integer values' do\n      let(:env) { { 'OVERCOMMIT_ENV_VAR' => 1, 'OVERCOMMIT_ENV_VAR_2' => 2 } }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::ConfigurationError\n        output.string.should =~ /`OVERCOMMIT_ENV_VAR`.*must be a string/i\n        output.string.should =~ /`OVERCOMMIT_ENV_VAR_2`.*must be a string/i\n      end\n    end\n\n    context 'and it is a hash with boolean values' do\n      let(:env) { { 'OVERCOMMIT_ENV_VAR' => true, 'OVERCOMMIT_ENV_VAR_2' => false } }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::ConfigurationError\n        output.string.should =~ /`OVERCOMMIT_ENV_VAR`.*must be a string/i\n        output.string.should =~ /`OVERCOMMIT_ENV_VAR_2`.*must be a string/i\n      end\n    end\n  end\n\n  context 'when hook has `processors` set' do\n    let(:concurrency) { 4 }\n\n    let(:config_hash) do\n      {\n        'concurrency' => concurrency,\n        'PreCommit' => {\n          'MyHook' => {\n            'enabled' => true,\n            'processors' => processors,\n          },\n        },\n      }\n    end\n\n    context 'and it is larger than `concurrency`' do\n      let(:processors) { concurrency + 1 }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::ConfigurationError\n      end\n    end\n\n    context 'and it is equal to `concurrency`' do\n      let(:processors) { concurrency }\n\n      it 'is valid' do\n        expect { subject }.not_to raise_error\n      end\n    end\n\n    context 'and it is less than `concurrency`' do\n      let(:processors) { concurrency - 1 }\n\n      it 'is valid' do\n        expect { subject }.not_to raise_error\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/default_configuration_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe 'default configuration' do\n  default_config =\n    begin\n      YAML.load_file(Overcommit::ConfigurationLoader::DEFAULT_CONFIG_PATH, aliases: true).to_hash\n    rescue ArgumentError\n      YAML.load_file(Overcommit::ConfigurationLoader::DEFAULT_CONFIG_PATH).to_hash\n    end\n\n  Overcommit::Utils.supported_hook_types.each do |hook_type|\n    hook_class = Overcommit::Utils.camel_case(hook_type)\n\n    Dir[File.join(Overcommit::HOOK_DIRECTORY, hook_type.tr('-', '_'), '*')].\n      map { |hook_file| Overcommit::Utils.camel_case(File.basename(hook_file, '.rb')) }.\n      each do |hook|\n      next if hook == 'Base'\n\n      context \"for the #{hook} #{hook_type} hook\" do\n        it 'exists in config/default.yml' do\n          default_config[hook_class][hook].should_not be_nil\n        end\n\n        it 'explicitly sets the enabled option' do\n          # Use variable names so it reads nicer in the RSpec output\n          hook_enabled_option_set = !default_config[hook_class][hook]['enabled'].nil?\n          all_hook_enabled_option_set = !default_config[hook_class]['ALL']['enabled'].nil?\n\n          (hook_enabled_option_set || all_hook_enabled_option_set).should == true\n        end\n\n        it 'defines a description' do\n          default_config[hook_class][hook]['description'].should_not be_nil\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/git_config_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::GitConfig do\n  describe '.comment_character' do\n    subject { described_class.comment_character }\n\n    context 'with no configuration' do\n      it 'should be \"#\"' do\n        repo do\n          `git config --local core.commentchar \"\"`\n          expect(subject).to eq '#'\n        end\n      end\n    end\n\n    context 'with custom configuration' do\n      it 'should be the configured character' do\n        repo do\n          `git config --local core.commentchar x`\n          expect(subject).to eq 'x'\n        end\n      end\n    end\n  end\n\n  describe '.hooks_path' do\n    subject { described_class.hooks_path }\n\n    context 'when not explicitly set' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it 'returns the default hook path' do\n        expect(subject).to eq File.expand_path(File.join('.git', 'hooks'))\n      end\n    end\n\n    context 'when explicitly set to an empty string' do\n      around do |example|\n        repo do\n          `git config --local core.hooksPath \"\"`\n          example.run\n        end\n      end\n\n      it 'returns the default hook path' do\n        expect(subject).to eq File.expand_path(File.join('.git', 'hooks'))\n      end\n    end\n\n    context 'when explicitly set to an absolute path' do\n      around do |example|\n        repo do\n          `git config --local core.hooksPath /etc/hooks`\n          example.run\n        end\n      end\n\n      it 'returns the absolute path' do\n        expect(subject).to eq File.absolute_path('/etc/hooks')\n      end\n    end\n\n    context 'when explicitly set to a relative path' do\n      around do |example|\n        repo do\n          `git config --local core.hooksPath my-hooks`\n          example.run\n        end\n      end\n\n      it 'returns the absolute path to the directory relative to the repo root' do\n        expect(subject).to eq File.expand_path('my-hooks')\n      end\n    end\n\n    context 'when explicitly set to a path starting with a tilde' do\n      around do |example|\n        repo do\n          `git config --local core.hooksPath ~/my-hooks`\n          example.run\n        end\n      end\n\n      it 'returns the absolute path to the folder in the users home path' do\n        expect(subject).to eq File.expand_path('~/my-hooks')\n        expect(subject).not_to include('~')\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/git_repo_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::GitRepo do\n  describe '.submodule_statuses' do\n    let(:options) { {} }\n    subject { described_class.submodule_statuses(options) }\n\n    context 'when repo contains no submodules' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when repo contains submodules' do\n      around do |example|\n        nested_submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        submodule = repo do\n          `git -c protocol.file.allow=always submodule add \\\n            #{nested_submodule} nested-sub 2>&1 > #{File::NULL}`\n          `git commit -m \"Add nested submodule\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`\n          example.run\n        end\n      end\n\n      it 'returns the submodule statuses' do\n        subject.map(&:path).should == ['sub']\n      end\n\n      context 'when recursive flag is specified' do\n        let(:options) { { recursive: true } }\n\n        it 'returns submodule statuses including nested submodules' do\n          subject.map(&:path).sort.should == ['sub', 'sub/nested-sub']\n        end\n      end\n    end\n  end\n\n  describe '.extract_modified_lines' do\n    let(:file) { 'file.txt' }\n    let(:options) { {} }\n\n    subject { described_class.extract_modified_lines(file, options) }\n\n    around do |example|\n      repo do\n        echo(\"Hello World\\nHow are you?\", file)\n        `git add file.txt`\n        `git commit -m \"Initial commit\"`\n        example.run\n      end\n    end\n\n    context 'when no lines were modified' do\n      it { should be_empty }\n    end\n\n    context 'when lines were added' do\n      before do\n        echo('Hello Again', file, append: true)\n      end\n\n      it 'includes the added lines' do\n        subject.to_a.should == [3]\n      end\n    end\n\n    context 'when lines were removed' do\n      before do\n        echo('Hello World', file)\n      end\n\n      it { should be_empty }\n    end\n  end\n\n  describe '.modified_files' do\n    let(:options) { {} }\n    subject { described_class.modified_files(options) }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    context 'when `staged` option is set' do\n      let(:options) { { staged: true } }\n\n      context 'when files were added' do\n        before do\n          touch 'added.txt'\n          `git add added.txt`\n        end\n\n        it { should == [File.expand_path('added.txt')] }\n      end\n\n      context 'when files were renamed' do\n        before do\n          touch 'file.txt'\n          `git add file.txt`\n          `git commit -m \"Initial commit\"`\n          `git mv file.txt renamed.txt`\n        end\n\n        it { should == [File.expand_path('renamed.txt')] }\n      end\n\n      context 'when files were modified' do\n        before do\n          touch 'file.txt'\n          `git add file.txt`\n          `git commit -m \"Initial commit\"`\n          echo('Modification', 'file.txt', append: true)\n          `git add file.txt`\n        end\n\n        it { should == [File.expand_path('file.txt')] }\n      end\n\n      context 'when files were deleted' do\n        before do\n          touch 'file.txt'\n          `git add file.txt`\n          `git commit -m \"Initial commit\"`\n          `git rm file.txt`\n        end\n\n        it { should == [] }\n      end\n\n      context 'when submodules were added' do\n        let(:submodule) do\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n          end\n        end\n\n        before do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`\n        end\n\n        it { should_not include File.expand_path('sub') }\n      end\n    end\n  end\n\n  describe '.list_files' do\n    let(:paths) { [] }\n    let(:options) { {} }\n    subject { described_class.list_files(paths, options) }\n\n    around do |example|\n      repo do\n        `git commit --allow-empty -m \"Initial commit\"`\n        example.run\n      end\n    end\n\n    context 'when path includes a submodule directory' do\n      let(:submodule_dir) { 'sub-repo' }\n\n      before do\n        submodule = repo do\n          `git commit --allow-empty -m \"Submodule commit\"`\n        end\n\n        `git -c protocol.file.allow=always submodule add \\\n          #{submodule} #{submodule_dir} 2>&1 > #{File::NULL}`\n        `git commit -m \"Add submodule\"`\n      end\n\n      it { should_not include(File.expand_path(submodule_dir)) }\n    end\n\n    context 'when listing contents of a directory' do\n      let(:dir) { 'some-dir' }\n      let(:paths) { [dir + File::SEPARATOR] }\n\n      before do\n        FileUtils.mkdir(dir)\n      end\n\n      context 'when directory is empty' do\n        it { should be_empty }\n      end\n\n      context 'when directory contains a file' do\n        let(:file) { \"#{dir}/file\" }\n\n        before do\n          touch(file)\n          `git add \"#{file}\"`\n          `git commit -m \"Add file\"`\n        end\n\n        context 'when path contains no spaces' do\n          it { should include(File.expand_path(file)) }\n        end\n\n        context 'when path contains spaces' do\n          let(:dir) { 'some dir' }\n\n          it { should include(File.expand_path(file)) }\n        end\n      end\n    end\n\n    context 'when the git ls-tree command fails for whatever reason' do\n      before do\n        result = double('result', success?: false, statuses: [1], stdouts: '', stderrs: '')\n        allow(Overcommit::Utils).\n          to receive(:execute).\n          with(%w[git ls-tree --name-only HEAD], args: []).\n          and_return(result)\n      end\n\n      it 'raises' do\n        expect { subject }.to raise_error Overcommit::Exceptions::Error\n      end\n    end\n  end\n\n  describe '.tracked?' do\n    subject { described_class.tracked?(file) }\n\n    around do |example|\n      repo do\n        touch 'untracked'\n        touch 'tracked'\n        `git add tracked`\n        `git commit -m \"Initial commit\"`\n        touch 'staged'\n        `git add staged`\n        example.run\n      end\n    end\n\n    context 'when file is untracked' do\n      let(:file) { 'untracked' }\n\n      it { should == false }\n    end\n\n    context 'when file is committed' do\n      let(:file) { 'tracked' }\n\n      it { should == true }\n    end\n\n    context 'when file is staged' do\n      let(:file) { 'staged' }\n\n      it { should == true }\n    end\n  end\n\n  describe '.all_files' do\n    subject { described_class.all_files }\n\n    let(:submodule) do\n      repo do\n        `git commit --allow-empty -m \"Initial commit\"`\n      end\n    end\n\n    around do |example|\n      repo do\n        touch 'untracked'\n        touch 'tracked'\n        `git add tracked`\n        `git commit -m \"Initial commit\"`\n        `git -c protocol.file.allow=always submodule add #{submodule} sub 2>&1 > #{File::NULL}`\n        touch 'staged'\n        `git add staged`\n        example.run\n      end\n    end\n\n    it { should include(*%w[tracked staged].map { |file| File.expand_path(file) }) }\n    it { should_not include File.expand_path('sub') }\n  end\n\n  describe '.initial_commit?' do\n    subject { described_class.initial_commit? }\n\n    context 'when there are no existing commits in the repository' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it { should == true }\n    end\n\n    context 'when there are commits in the repository' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          example.run\n        end\n      end\n\n      it { should == false }\n    end\n  end\n\n  describe '.staged_submodule_removals' do\n    subject { described_class.staged_submodule_removals }\n\n    around do |example|\n      submodule = repo do\n        `git commit --allow-empty -m \"Submodule commit\"`\n      end\n\n      repo do\n        `git -c protocol.file.allow=always submodule add #{submodule} sub-repo 2>&1 > #{File::NULL}`\n        `git commit -m \"Initial commit\"`\n        example.run\n      end\n    end\n\n    context 'when there are no submodule removals staged' do\n      it { should be_empty }\n    end\n\n    context 'when there are submodule additions staged' do\n      before do\n        another_submodule = repo do\n          `git commit --allow-empty -m \"Another submodule\"`\n        end\n\n        `git -c protocol.file.allow=always submodule add \\\n          #{another_submodule} another-sub-repo 2>&1 > #{File::NULL}`\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when there is one submodule removal staged' do\n      before do\n        `git rm sub-repo`\n      end\n\n      it 'returns the submodule that was removed' do\n        subject.size.should == 1\n        subject.first.tap do |sub|\n          sub.path.should == 'sub-repo'\n          File.directory?(sub.url).should == true\n        end\n      end\n    end\n\n    context 'when there are multiple submodule removals staged' do\n      before do\n        another_submodule = repo do\n          `git commit --allow-empty -m \"Another submodule\"`\n        end\n\n        `git -c protocol.file.allow=always submodule add \\\n          #{another_submodule} yet-another-sub-repo 2>&1 > #{File::NULL}`\n        `git commit -m \"Add yet another submodule\"`\n        `git rm sub-repo`\n        `git rm yet-another-sub-repo`\n      end\n\n      it 'returns all submodules that were removed' do\n        subject.size.should == 2\n        subject.map(&:path).sort.should == %w[sub-repo yet-another-sub-repo]\n      end\n    end\n  end\n\n  describe '.branches_containing_commit' do\n    subject { described_class.branches_containing_commit(commit_ref) }\n\n    around do |example|\n      repo do\n        `git checkout -b master > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Initial commit\"`\n        `git checkout -b topic > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Another commit\"`\n        example.run\n      end\n    end\n\n    context 'when only one branch contains the commit' do\n      let(:commit_ref) { 'topic' }\n\n      it 'should return only that branch' do\n        subject.size.should == 1\n        subject[0].should == 'topic'\n      end\n    end\n\n    context 'when more than one branch contains the commit' do\n      let(:commit_ref) { 'master' }\n\n      it 'should return all branches containing the commit' do\n        subject.size.should == 2\n        subject.sort.should == %w[master topic]\n      end\n    end\n\n    context 'when no branches contain the commit' do\n      let(:commit_ref) { 'HEAD' }\n\n      before do\n        `git checkout --detach > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Detached HEAD\"`\n      end\n\n      it { should be_empty }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::Base do\n  let(:config) { double('config') }\n  let(:context) { double('context') }\n  let(:hook) { described_class.new(config, context) }\n\n  describe '#run_and_transform' do\n    let(:var_name) { 'OVERCOMMIT_TEST_HOOK_VAR' }\n    let(:hook_config) { {} }\n\n    before do\n      config.stub(:for_hook).and_return(hook_config)\n      hook.stub(:run) { ENV[var_name] == 'pass' ? :pass : :fail }\n    end\n\n    subject { hook.run_and_transform }\n\n    context 'when no env configuration option is specified' do\n      let(:hook_config) { {} }\n\n      it 'does not modify the environment' do\n        subject.first.should == :fail\n      end\n    end\n\n    context 'when env configuration option is specified' do\n      let(:hook_config) { { 'env' => { var_name => 'pass' } } }\n\n      it 'modifies the environment' do\n        subject.first.should == :pass\n      end\n    end\n  end\n\n  describe '#run?' do\n    let(:modified_files) { [] }\n    let(:hook_config) do\n      {\n        'enabled' => enabled,\n        'requires_files' => requires_files,\n      }\n    end\n\n    before do\n      config.stub(:for_hook).and_return(hook_config)\n      context.stub(:modified_files).and_return(modified_files)\n    end\n\n    subject { hook.run? }\n\n    context 'enabled is true, requires_files is false, modified_files empty' do\n      let(:enabled) { true }\n      let(:requires_files) { false }\n\n      it { subject.should == true }\n    end\n\n    context 'enabled is false, requires_files is false, modified_files empty' do\n      let(:enabled) { false }\n      let(:requires_files) { false }\n\n      it { subject.should == false }\n    end\n\n    context 'enabled is true, requires_files is true, modified_files is not empty' do\n      let(:enabled) { true }\n      let(:requires_files) { true }\n      let(:modified_files) { ['file1'] }\n\n      it { subject.should == true }\n    end\n\n    context 'enabled is true, requires_files is false, modified_files is not empty' do\n      let(:enabled) { true }\n      let(:requires_files) { false }\n      let(:modified_files) { ['file1'] }\n\n      it { subject.should == true }\n    end\n\n    context 'with exclude_branches specified' do\n      let(:current_branch) { 'test-branch' }\n      let(:hook_config) do\n        {\n          'enabled' => true,\n          'requires_files' => false,\n          'exclude_branches' => exclude_branches\n        }\n      end\n\n      before do\n        allow(Overcommit::GitRepo).\n          to receive(:current_branch).\n          and_return(current_branch)\n      end\n\n      context 'exclude_branches is nil' do\n        let(:exclude_branches) { nil }\n\n        it { subject.should == true }\n      end\n\n      context 'exact match between exclude_branches and current_branch' do\n        let(:exclude_branches) { ['test-branch'] }\n\n        it { subject.should == false }\n      end\n\n      context 'partial match between exclude_branches and current_branch' do\n        let(:exclude_branches) { ['test-*'] }\n\n        it { subject.should == false }\n      end\n\n      context 'non-match between exclude_branches and current_branch' do\n        let(:exclude_branches) { ['no-test-*'] }\n\n        it { subject.should == true }\n      end\n    end\n  end\n\n  context '#skip?' do\n    before do\n      config.stub(:for_hook).and_return(hook_config)\n    end\n\n    subject { hook.skip? }\n\n    context 'with skip_if not specified' do\n      let(:hook_config) do\n        { 'skip' => skip }\n      end\n\n      context 'with skip true' do\n        let(:skip) { true }\n\n        it { subject.should == true }\n      end\n\n      context 'with skip false' do\n        let(:skip) { false }\n\n        it { subject.should == false }\n      end\n    end\n\n    context 'with skip_if specified' do\n      before do\n        result = Overcommit::Subprocess::Result.new(success ? 0 : 1, '', '')\n        allow(Overcommit::Utils).to receive(:execute).and_return(result)\n      end\n\n      let(:hook_config) do\n        { 'skip' => skip, 'skip_if' => ['bash', '-c', '! which my-executable'] }\n      end\n\n      context 'with skip true and skip_if returning true' do\n        let(:skip) { true }\n        let(:success) { true }\n\n        it { subject.should == true }\n      end\n\n      context 'with skip true and skip_if returning false' do\n        let(:skip) { true }\n        let(:success) { false }\n\n        it { subject.should == true }\n      end\n\n      context 'with skip false and skip_if returning true' do\n        let(:skip) { false }\n        let(:success) { true }\n\n        it { subject.should == true }\n      end\n\n      context 'with skip false and skip_if returning false' do\n        let(:skip) { false }\n        let(:success) { false }\n\n        it { subject.should == false }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/capitalized_subject_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::CapitalizedSubject do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg.split(\"\\n\"))\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should pass }\n  end\n\n  context 'when subject starts with a capital letter' do\n    let(:commit_msg) { <<-MSG }\nInitial commit\n\nMostly cats so far.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject starts with a utf-8 capital letter' do\n    let(:commit_msg) { <<-MSG }\nÅrsgång\n\nMostly cats so far.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject starts with punctuation and a capital letter' do\n    let(:commit_msg) { <<-MSG }\n\"Initial\" commit\n\nMostly cats so far.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject starts with a lowercase letter' do\n    let(:commit_msg) { <<-MSG }\ninitial commit\n\nI forget about commit message standards and decide to not capitalize my\nsubject. Still mostly cats so far.\n    MSG\n\n    it { should warn }\n  end\n\n  context 'when subject starts with a utf-8 lowercase letter' do\n    let(:commit_msg) { <<-MSG }\nårsgång\n\nI forget about commit message standards and decide to not capitalize my\nsubject. Still mostly cats so far.\n    MSG\n\n    it { should warn }\n  end\n\n  context 'when subject starts with punctuation and a lowercase letter' do\n    let(:commit_msg) { <<-MSG }\n\"initial\" commit\n\nI forget about commit message standards and decide to not capitalize my\nsubject. Still mostly cats so far.\n    MSG\n\n    it { should warn }\n  end\n\n  context 'when subject starts with special \"fixup!\" prefix' do\n    let(:commit_msg) { <<-MSG }\nfixup! commit\n\nThis was created by running git commit --fixup=...\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject starts with special \"squash!\" prefix' do\n    let(:commit_msg) { <<-MSG }\nsquash! commit\n\nThis was created by running git commit --squash=...\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when first line of commit message is an empty line' do\n    let(:commit_msg) { <<-MSG }\n\nThere was no first line\n\nThis is a mistake.\n    MSG\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/empty_message_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::EmptyMessage do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:empty_message?).and_return(commit_msg.strip.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should fail_hook }\n  end\n\n  context 'when commit message contains only whitespace' do\n    let(:commit_msg) { ' ' }\n\n    it { should fail_hook }\n  end\n\n  context 'when commit message is not empty' do\n    let(:commit_msg) { 'Some commit message' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/gerrit_change_id_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::GerritChangeId do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:commit_msg_file) { Tempfile.new('commit-msg') }\n\n  before do\n    commit_msg_file.write(commit_msg)\n    commit_msg_file.close\n    context.stub(:commit_message_file).and_return(commit_msg_file.path)\n  end\n\n  context 'when the commit message contains no Change-Id' do\n    let(:commit_msg) { 'Add code to repo' }\n\n    it { should pass }\n\n    it 'adds a Change-Id to the commit message' do\n      subject.run\n      File.open(commit_msg_file.path, 'r').read.should =~ /Change-Id/\n    end\n  end\n\n  context 'when the commit message already contains a Change-Id' do\n    let(:commit_msg) { \"Add code to repo\\n\\nChange-Id: I9f2b5528fa20ac91a55bbe9371e76a12dd1cce11\" }\n\n    it { should pass }\n\n    it 'does nothing' do\n      before = File.open(commit_msg_file.path, 'r').read\n      subject.run\n      after = File.open(commit_msg_file.path, 'r').read\n      before.should == after\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/hard_tabs_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::HardTabs do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message).and_return(commit_msg)\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should pass }\n  end\n\n  context 'when message contains hard tabs' do\n    let(:commit_msg) { \"This is a hard-tab\\tcommit message\" }\n\n    it { should warn }\n  end\n\n  context 'when message does not contain hard tabs' do\n    let(:commit_msg) { 'No hard tabs to be found' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/message_format_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::MessageFormat do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg.lines.to_a)\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when pattern is empty' do\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'CommitMsg' => {\n                        'MessageFormat' => {\n                          'pattern' => nil\n                        }\n                      }\n      ))\n    end\n\n    let(:commit_msg) { 'Some Message' }\n\n    it { should pass }\n  end\n\n  context 'when message does not match the pattern' do\n    let(:commit_msg) { 'Some Message' }\n\n    expected_message = [\n      'Commit message pattern mismatch.',\n      'Expected : <Issue Id> | <Commit Message Description> | <Developer(s)>',\n      'Sample : DEFECT-1234 | Refactored Onboarding flow | John Doe'\n    ].join(\"\\n\")\n\n    it { should fail_hook expected_message }\n  end\n\n  context 'when multiline message matches the pattern' do\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'CommitMsg' => {\n                        'MessageFormat' => {\n                          'pattern' => '^Some .* Message$'\n                        }\n                      }\n      ))\n    end\n\n    let(:commit_msg) { \"Some \\n multiline \\n Message\" }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/russian_novel_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::RussianNovel do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg)\n  end\n\n  context 'when message contains fewer than 30 lines' do\n    let(:commit_msg) { ['A single line'] * 10 }\n\n    it { should pass }\n  end\n\n  context 'when message contains at least 30 lines' do\n    let(:commit_msg) { ['A single line'] * 30 }\n\n    it { should warn }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/single_line_subject_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::SingleLineSubject do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg.split(\"\\n\"))\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should pass }\n  end\n\n  context 'when subject is separated from body by a blank line' do\n    let(:commit_msg) { <<-MSG }\n      Initial commit\n\n      Mostly cats so far.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject is not kept to one line' do\n    let(:commit_msg) { <<-MSG }\n      Initial commit where I forget about commit message\n      standards and decide to hard-wrap my subject\n\n      Still mostly cats so far.\n    MSG\n\n    it { should warn }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/spell_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::SpellCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:uncommented_commit_msg_file)\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when hunspell exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    context 'with no misspellings' do\n      before do\n        result.stub(:stdout).and_return(<<-MSG)\n@(#) International Ispell Version 3.2.06 (but really Hunspell 1.3.3)\n*\n*\n*\n        MSG\n      end\n\n      it { should pass }\n    end\n\n    context 'with misspellings' do\n      context 'with suggestions' do\n        before do\n          result.stub(:stdout).and_return(<<-MSG)\n@(#) International Ispell Version 3.2.06 (but really Hunspell 1.3.3)\n*\n& msg 10 4: MSG, mag, ms, mg, meg, mtg, mug, mpg, mfg, ms g\n*\n          MSG\n        end\n\n        it { should warn(/^Potential misspelling: \\w+. Suggestions: .+$/) }\n      end\n\n      context 'with no suggestions' do\n        before do\n          result.stub(:stdout).and_return(<<-MSG)\n@(#) International Ispell Version 3.2.06 (but really Hunspell 1.3.3)\n*\n# supercalifragilisticexpialidocious 4\n*\n          MSG\n        end\n\n        it { should warn(/^Potential misspelling: \\w+.$/) }\n      end\n    end\n  end\n\n  context 'when hunspell exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: false, stderr: <<-MSG)\nCan't open affix or dictionary files for dictionary named \"foo\".\n      MSG\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/text_width_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::TextWidth do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg.lines.to_a)\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should pass }\n  end\n\n  context 'when subject is longer than 60 characters' do\n    let(:commit_msg) { 'A' * 61 }\n\n    it { should warn /subject/ }\n  end\n\n  context 'when subject is 60 characters or fewer' do\n    let(:commit_msg) { 'A' * 60 }\n\n    it { should pass }\n  end\n\n  context 'when subject starts with special \"fixup!\" and is longer than 60 characters' do\n    let(:commit_msg) { 'fixup! ' + 'A' * 60 }\n\n    it { should pass }\n  end\n\n  context 'when subject starts with special \"squash!\" and is longer than 60 characters' do\n    let(:commit_msg) { 'squash! ' + 'A' * 60 }\n\n    it { should pass }\n  end\n\n  context 'when the subject is 60 characters followed by a newline' do\n    let(:commit_msg) { <<-MSG }\n      This is 60 characters, or 61 if the newline is counted\n\n      A reasonable line.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when a line in the message is 72 characters followed by a newline' do\n    let(:commit_msg) { <<-MSG }\n      Some summary\n\n      This line has 72 characters, but with newline it has 73 characters\n      That shouldn't be a problem.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when a line in the message is longer than 72 characters' do\n    let(:commit_msg) { <<-MSG }\n      Some summary\n\n      This line is longer than 72 characters which is clearly be seen by count.\n    MSG\n\n    it { should warn('Line 3 of commit message has > 72 characters') }\n  end\n\n  context 'when all lines in the message are fewer than 72 characters' do\n    let(:commit_msg) { <<-MSG }\n      Some summary\n\n      A reasonable line.\n\n      Another reasonable line.\n    MSG\n\n    it { should pass }\n  end\n\n  context 'when subject and a line in the message is longer than the limits' do\n    let(:commit_msg) { <<-MSG }\n      A subject line that is way too long. A subject line that is way too long.\n\n      A message line that is way too long. A message line that is way too long.\n    MSG\n\n    it { should warn /subject.*<= 60.*\\n.*line 3.*> 72.*/im }\n  end\n\n  context 'when custom lengths are specified' do\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'CommitMsg' => {\n                        'TextWidth' => {\n                          'max_subject_width' => 70,\n                          'min_subject_width' => 4,\n                          'max_body_width' => 80\n                        }\n                      }\n      ))\n    end\n\n    context 'when subject is longer than 70 characters' do\n      let(:commit_msg) { 'A' * 71 }\n\n      it { should warn /subject must be <= 70/ }\n    end\n\n    context 'when subject is less than 4 characters' do\n      let(:commit_msg) { 'A' * 3 }\n\n      it { should warn /subject must be >= 4/ }\n    end\n\n    context 'when subject is 70 characters or fewer' do\n      let(:commit_msg) { 'A' * 70 }\n\n      it { should pass }\n    end\n\n    context 'when a line in the message is longer than 80 characters' do\n      let(:commit_msg) { <<-MSG }\n        Some summary\n\n        This line is longer than #{'A' * 80} characters.\n      MSG\n\n      it { should warn 'Line 3 of commit message has > 80 characters' }\n    end\n\n    context 'when all lines in the message are fewer than 80 characters' do\n      let(:commit_msg) { <<-MSG }\n        Some summary\n\n        A reasonable line.\n\n        Another reasonable line.\n      MSG\n\n      it { should pass }\n    end\n\n    context 'when subject and a line in the message is longer than the limits' do\n      let(:commit_msg) { <<-MSG }\n        A subject line that is way too long. A subject line that is way too long.\n\n        This line is longer than #{'A' * 80} characters.\n      MSG\n\n      it { should warn /subject.*<= 70.*\\n.*line 3.*> 80/im }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/commit_msg/trailing_period_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::CommitMsg::TrailingPeriod do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    context.stub(:commit_message_lines).and_return(commit_msg.split(\"\\n\"))\n    context.stub(:empty_message?).and_return(commit_msg.empty?)\n  end\n\n  context 'when commit message is empty' do\n    let(:commit_msg) { '' }\n\n    it { should pass }\n  end\n\n  context 'when subject contains a trailing period' do\n    let(:commit_msg) { 'This subject has a period.' }\n\n    it { should warn }\n  end\n\n  context 'when subject does not contain a trailing period' do\n    let(:commit_msg) { 'This subject has no period' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::Base do\n  let(:config) { double('config') }\n  let(:context) { double('context') }\n  let(:hook) { described_class.new(config, context) }\n\n  let(:hook_config) { {} }\n\n  before do\n    config.stub(:for_hook).and_return(hook_config)\n  end\n\n  describe '#skip_file_checkout?' do\n    subject { hook.skip_file_checkout? }\n\n    context 'when skip_file_checkout is not set' do\n      it { should == true }\n    end\n\n    context 'when skip_file_checkout is set to false' do\n      let(:hook_config) { { 'skip_file_checkout' => false } }\n\n      it { should == false }\n    end\n\n    context 'when skip_file_checkout is set to true' do\n      let(:hook_config) { { 'skip_file_checkout' => true } }\n\n      it { should == true }\n    end\n  end\n\n  describe '#enabled?' do\n    subject { hook.enabled? }\n\n    shared_examples 'hook enabled' do |enabled, skip_file_checkout, file_checkout, expected|\n      context \"when enabled is set to #{enabled}\" do\n        context \"when skip_file_checkout is set to #{skip_file_checkout}\" do\n          context \"when file_checkout? is #{file_checkout}\" do\n            let(:hook_config) do\n              { 'enabled' => enabled, 'skip_file_checkout' => skip_file_checkout }\n            end\n\n            before do\n              context.stub(:file_checkout?).and_return(file_checkout)\n            end\n\n            it { should == expected }\n          end\n        end\n      end\n    end\n\n    include_examples 'hook enabled', true,  true,  true,  false\n    include_examples 'hook enabled', true,  true,  false, true\n    include_examples 'hook enabled', true,  false, true,  true\n    include_examples 'hook enabled', true,  false, false, true\n    include_examples 'hook enabled', false, true,  true,  false\n    include_examples 'hook enabled', false, true,  false, false\n    include_examples 'hook enabled', false, false, true,  false\n    include_examples 'hook enabled', false, false, false, false\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/bower_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::BowerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bower install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bower install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        bower EMALFORMED Failed to read bower.json\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/bundle_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::BundleInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bundle install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bundle install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/composer_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::ComposerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when composer install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when composer install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Composer could not find a composer.json file')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/index_tags_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::IndexTags do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:execute_in_background)\n  end\n\n  it { should pass }\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/npm_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::NpmInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when npm install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when npm install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: \"npm ERR! install Couldn't read dependencies\")\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/submodule_status_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::SubmoduleStatus do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:submodule_status) { double('submodule_status') }\n\n  before do\n    submodule_status.stub(:path).and_return('sub')\n    subject.stub(:submodule_statuses).and_return([submodule_status])\n  end\n\n  context 'when submodule is up to date' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should pass }\n  end\n\n  context 'when submodule is uninitialized' do\n    before do\n      submodule_status.stub(uninitialized?: true,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/uninitialized/) }\n  end\n\n  context 'when submodule is outdated' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: true,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/out of date/) }\n  end\n\n  context 'when submodule has merge conflicts' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: true)\n    end\n\n    it { should warn(/merge conflicts/) }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_checkout/yarn_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCheckout::YarnInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when yarn install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when yarn install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: %{error An unexpected error occurred: ...})\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/bower_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::BowerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bower install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bower install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        bower EMALFORMED Failed to read bower.json\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/bundle_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::BundleInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bundle install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bundle install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/commitplease_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::Commitplease do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when commitplease exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n      result.stub(:stderr).and_return('')\n    end\n\n    it { should pass }\n  end\n\n  context 'when commitplease exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        - First line must be <type>(<scope>): <subject>\n        Need an opening parenthesis: (\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/composer_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::ComposerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when composer install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when composer install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Composer could not find a composer.json file')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/git_guilt_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::GitGuilt do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when git-guilt exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(initial_commit?: false, execute: result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'with output' do\n      before do\n        result.stub(:stdout).and_return('GitGuilt Tester +++')\n      end\n\n      it { should warn }\n    end\n  end\n\n  context 'when git-guilt exits unsuccessfully' do\n    before do\n      result = double('result')\n      result.stub(success?: false, stderr: '')\n      subject.stub(initial_commit?: false, execute: result)\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when there is no previous commit' do\n    before do\n      context.stub(:initial_commit?).and_return(true)\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/index_tags_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::IndexTags do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:execute_in_background)\n  end\n\n  it { should pass }\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/npm_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::NpmInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when npm install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when npm install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: \"npm ERR! install Couldn't read dependencies\")\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/submodule_status_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::SubmoduleStatus do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:submodule_status) { double('submodule_status') }\n\n  before do\n    submodule_status.stub(:path).and_return('sub')\n    subject.stub(:submodule_statuses).and_return([submodule_status])\n  end\n\n  context 'when submodule is up to date' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should pass }\n  end\n\n  context 'when submodule is uninitialized' do\n    before do\n      submodule_status.stub(uninitialized?: true,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/uninitialized/) }\n  end\n\n  context 'when submodule is outdated' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: true,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/out of date/) }\n  end\n\n  context 'when submodule has merge conflicts' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: true)\n    end\n\n    it { should warn(/merge conflicts/) }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_commit/yarn_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostCommit::YarnInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when yarn install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when yarn install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: %{error An unexpected error occurred: ...})\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/bower_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::BowerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bower install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bower install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        bower EMALFORMED Failed to read bower.json\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/bundle_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::BundleInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bundle install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bundle install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/composer_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::ComposerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when composer install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when composer install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Composer could not find a composer.json file')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/index_tags_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::IndexTags do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:execute_in_background)\n  end\n\n  it { should pass }\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/npm_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::NpmInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when npm install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when npm install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: \"npm ERR! install Couldn't read dependencies\")\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/submodule_status_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::SubmoduleStatus do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:submodule_status) { double('submodule_status') }\n\n  before do\n    submodule_status.stub(:path).and_return('sub')\n    subject.stub(:submodule_statuses).and_return([submodule_status])\n  end\n\n  context 'when submodule is up to date' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should pass }\n  end\n\n  context 'when submodule is uninitialized' do\n    before do\n      submodule_status.stub(uninitialized?: true,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/uninitialized/) }\n  end\n\n  context 'when submodule is outdated' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: true,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/out of date/) }\n  end\n\n  context 'when submodule has merge conflicts' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: true)\n    end\n\n    it { should warn(/merge conflicts/) }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_merge/yarn_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::YarnInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when yarn install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when yarn install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: %{error An unexpected error occurred: ...})\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/bower_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::BowerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bower install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bower install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        bower EMALFORMED Failed to read bower.json\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/bundle_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::BundleInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when bundle install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when bundle install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Could not locate Gemfile or .bundle/ directory')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/composer_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::ComposerInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when composer install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when composer install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stdout: 'Composer could not find a composer.json file')\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/index_tags_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostMerge::IndexTags do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:execute_in_background)\n  end\n\n  it { should pass }\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/npm_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::NpmInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when npm install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when npm install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: \"npm ERR! install Couldn't read dependencies\")\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/submodule_status_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::SubmoduleStatus do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:submodule_status) { double('submodule_status') }\n\n  before do\n    submodule_status.stub(:path).and_return('sub')\n    subject.stub(:submodule_statuses).and_return([submodule_status])\n  end\n\n  context 'when submodule is up to date' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should pass }\n  end\n\n  context 'when submodule is uninitialized' do\n    before do\n      submodule_status.stub(uninitialized?: true,\n                            outdated?: false,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/uninitialized/) }\n  end\n\n  context 'when submodule is outdated' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: true,\n                            merge_conflict?: false)\n    end\n\n    it { should warn(/out of date/) }\n  end\n\n  context 'when submodule has merge conflicts' do\n    before do\n      submodule_status.stub(uninitialized?: false,\n                            outdated?: false,\n                            merge_conflict?: true)\n    end\n\n    it { should warn(/merge conflicts/) }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/post_rewrite/yarn_install_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PostRewrite::YarnInstall do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when yarn install exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when yarn install exits unsuccessfully' do\n    before do\n      result.stub(success?: false, stderr: %{error An unexpected error occurred: ...})\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/author_email_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::AuthorEmail do\n  let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:result) { double('result') }\n\n  shared_examples_for 'author email check' do\n    context 'when user has no email' do\n      let(:email) { '' }\n\n      it { should fail_hook }\n    end\n\n    context 'when user has an invalid email' do\n      let(:email) { 'Invalid Email' }\n\n      it { should fail_hook }\n    end\n\n    context 'when user has a valid email' do\n      let(:email) { 'email@example.com' }\n\n      it { should pass }\n    end\n\n    context 'when a custom pattern is specified' do\n      let(:config) do\n        super().merge(Overcommit::Configuration.new(\n                        'PreCommit' => {\n                          'AuthorEmail' => {\n                            'pattern' => '^[^@]+@brigade\\.com$'\n                          }\n                        }\n        ))\n      end\n\n      context 'and the email does not match the pattern' do\n        let(:email) { 'email@example.com' }\n\n        it { should fail_hook }\n      end\n\n      context 'and the email matches the pattern' do\n        let(:email) { 'email@brigade.com' }\n\n        it { should pass }\n      end\n    end\n  end\n\n  context 'when email is set via config' do\n    before do\n      result.stub(:stdout).and_return(email)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it_should_behave_like 'author email check'\n  end\n\n  context 'when email is set via environment variable' do\n    around do |example|\n      Overcommit::Utils.with_environment 'GIT_AUTHOR_EMAIL' => email do\n        example.run\n      end\n    end\n\n    it_should_behave_like 'author email check'\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/author_name_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::AuthorName do\n  let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:result) { double('result') }\n\n  shared_examples_for 'author name check' do\n    context 'when user has no name' do\n      let(:name) { '' }\n\n      it { should fail_hook }\n    end\n\n    context 'when user has only a first name' do\n      let(:name) { 'John' }\n\n      it { should pass }\n    end\n\n    context 'when user has first and last name' do\n      let(:name) { 'John Doe' }\n\n      it { should pass }\n    end\n  end\n\n  context 'when name is set via config' do\n    before do\n      result.stub(:stdout).and_return(name)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it_should_behave_like 'author name check'\n  end\n\n  context 'when name is set via environment variable' do\n    around do |example|\n      Overcommit::Utils.with_environment 'GIT_AUTHOR_NAME' => name do\n        example.run\n      end\n    end\n\n    it_should_behave_like 'author name check'\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/berksfile_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::BerksfileCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when Berksfile.lock is ignored' do\n    around do |example|\n      repo do\n        touch 'Berksfile.lock'\n        echo('Berksfile.lock', '.gitignore')\n        `git add .gitignore`\n        `git commit -m \"Ignore Berksfile.lock\"`\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when Berksfile.lock is not ignored' do\n    let(:result) { double('result') }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      result.stub(success?: success, stderr: 'Berkshelf error message')\n      subject.stub(:execute).and_call_original\n      subject.stub(:execute).with(%w[berks list --quiet]).and_return(result)\n    end\n\n    context 'and `berks list` exits unsuccessfully' do\n      let(:success) { false }\n\n      it { should fail_hook }\n    end\n\n    context 'and `berks list` exits successfully' do\n      let(:success) { true }\n\n      it { should pass }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/broken_symlinks_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::BrokenSymlinks do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  let(:staged_file) { 'staged-file.txt' }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  before do\n    Overcommit::Utils.stub(:broken_symlink?).with(staged_file).and_return(broken)\n  end\n\n  context 'when the symlink is broken' do\n    let(:broken) { true }\n\n    it { should fail_hook }\n  end\n\n  context 'when the symlink is not broken' do\n    let(:broken) { false }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/bundle_audit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::BundleAudit do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when Gemfile.lock is ignored' do\n    around do |example|\n      repo do\n        touch 'Gemfile.lock'\n        echo('Gemfile.lock', '.gitignore')\n        `git add .gitignore`\n        `git commit -m \"Ignore Gemfile.lock\"`\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when Gemfile.lock is not ignored' do\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      subject.stub(:execute).with(%w[git ls-files -o -i --exclude-standard -- Gemfile.lock]).\n        and_return(double(stdout: ''))\n      subject.stub(:execute).with(%w[bundle-audit]).and_return(result)\n    end\n\n    context 'and it reports some outdated gems' do\n      let(:result) do\n        double(\n          success?: false,\n          stdout: <<-MSG\nName: rest-client\nVersion: 1.6.9\nAdvisory: CVE-2015-1820\nCriticality: Unknown\nURL: https://github.com/rest-client/rest-client/issues/369\nTitle: rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses\nSolution: upgrade to >= 1.8.0\nName: rest-client\nVersion: 1.6.9\nAdvisory: CVE-2015-3448\nCriticality: Unknown\nURL: http://www.osvdb.org/show/osvdb/117461\nTitle: Rest-Client Gem for Ruby logs password information in plaintext\nSolution: upgrade to >= 1.7.3\nVulnerabilities found!\n          MSG\n        )\n      end\n\n      it { should warn }\n    end\n\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nInsecure Source URI found: git://github.com/xxx/overcommit.git\nVulnerabilities found!\n        MSG\n      )\n    end\n\n    it { should warn }\n\n    context 'and it reports bundle up to date' do\n      let(:result) do\n        double(success?: true, stdout: 'No vulnerabilities found')\n      end\n\n      it { should pass }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/bundle_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::BundleCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when Gemfile.lock is ignored' do\n    around do |example|\n      repo do\n        touch 'Gemfile.lock'\n        echo('Gemfile.lock', '.gitignore')\n        `git add .gitignore`\n        `git commit -m \"Ignore Gemfile.lock\"`\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when Gemfile.lock is not ignored' do\n    let(:result) { double('result') }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      result.stub(success?: success, stdout: 'Bundler error message')\n      subject.stub(:execute).with(%w[git ls-files -o -i --exclude-standard]).\n                             and_return(double(stdout: ''))\n      subject.stub(:execute).with(%w[bundle check]).and_return(result)\n    end\n\n    context 'and bundle check exits unsuccessfully' do\n      let(:success) { false }\n\n      it { should fail_hook }\n    end\n\n    context 'and bundle check exits successfully' do\n      let(:success) { true }\n\n      it { should pass }\n\n      context 'and there was a change to the Gemfile.lock' do\n        before do\n          subject.stub(:execute).with(%w[bundle check]) do\n            echo('stuff', 'Gemfile.lock')\n            double(success?: true)\n          end\n        end\n\n        it { should fail_hook }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/bundle_outdated_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::BundleOutdated do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when Gemfile.lock is ignored' do\n    around do |example|\n      repo do\n        touch 'Gemfile.lock'\n        echo('Gemfile.lock', '.gitignore')\n        `git add .gitignore`\n        `git commit -m \"Ignore Gemfile.lock\"`\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when Gemfile.lock is not ignored' do\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      subject.stub(:execute).with(%w[git ls-files -o -i --exclude-standard]).\n                             and_return(double(stdout: ''))\n      subject.stub(:execute).with(%w[bundle outdated --strict --parseable]).\n                             and_return(result)\n    end\n\n    context 'and it reports some outdated gems' do\n      let(:result) do\n        double(stdout: <<-MSG\nWarning: the running version of Bundler is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.\nairbrake (newest 5.3.0, installed 5.2.3, requested ~> 5.0)\naws-sdk (newest 2.3.3, installed 2.3.1, requested ~> 2)\nfont-awesome-rails (newest 4.6.2.0, installed 4.6.1.0)\nmechanize (newest 2.7.4, installed 2.1.1)\nminimum-omniauth-scaffold (newest 0.4.3, installed 0.4.1)\nairbrake-ruby (newest 1.3.0, installed 1.2.4)\naws-sdk-core (newest 2.3.3, installed 2.3.1)\naws-sdk-resources (newest 2.3.3, installed 2.3.1)\nconfig (newest 1.1.1, installed 1.1.0)\nruby_parser (newest 3.8.2, installed 3.8.1)\n        MSG\n        )\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports bundle up to date' do\n      let(:result) do\n        double(stdout: <<-MSG\nWarning: the running version of Bundler is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.\n        MSG\n        )\n      end\n\n      it { should pass }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/case_conflicts_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::CaseConflicts do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    Overcommit::GitRepo.stub(:initial_commit?).and_return(false)\n    Overcommit::GitRepo.stub(:list_files).and_return(%w[foo])\n  end\n\n  context 'when a new file conflicts with an existing file' do\n    before do\n      subject.stub(:applicable_files).and_return(%w[Foo])\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when a new file conflicts with another new file' do\n    before do\n      subject.stub(:applicable_files).and_return(%w[bar Bar])\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when there are no conflicts' do\n    before do\n      subject.stub(:applicable_files).and_return(%w[bar baz])\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/chamber_compare_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ChamberCompare do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(['my_settings.yml'])\n  end\n\n  context 'when chamber exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when chamber exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('Some error message')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/chamber_security_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ChamberSecurity do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(['my_settings.yml'])\n  end\n\n  context 'when chamber exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when chamber exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('Some error message')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/chamber_verification_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ChamberVerification do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(['my_settings.yml'])\n  end\n\n  context 'when chamber exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when chamber exits unsuccessfully but because of missing keys' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('')\n      result.stub(:stderr).and_return('no signature key was found')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when chamber exits unsuccessfully via standard out' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\n\n  context 'when chamber exits unsuccessfully via standard error' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return('')\n      result.stub(:stderr).and_return('Some error message')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/code_spell_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::CodeSpellCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when code-spell-check exists successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return('')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when code-spell-check exists unsuccessfully via standard error' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('')\n      result.stub(:stderr).and_return(\n        \"file1.rb:35: inkorrectspelling\\n✗ Errors in code spellchecking\"\n      )\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/coffee_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::CoffeeLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.coffee file2.coffee])\n  end\n\n  context 'when coffeelint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no warnings' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          path,lineNumber,lineNumberEnd,level,message\n        OUT\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          path,lineNumber,lineNumberEnd,level,message\n          file1.coffee,31,,warn,Comprehensions must have parentheses around them\n        OUT\n      end\n\n      it { should warn }\n    end\n  end\n\n  context 'when coffeelint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          path,lineNumber,lineNumberEnd,level,message\n          file1.coffee,17,,error,Duplicate key defined in object or class\n        OUT\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/cook_style_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::CookStyle do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when cookstyle exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n\n    context 'and it printed warnings to stderr' do\n      before do\n        result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n          warning: parser/current is loading parser/ruby21, which recognizes\n          warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n          warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n        MSG\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when cookstyle exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:1:1: W: Useless assignment to variable - my_var.',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should warn }\n\n      context 'and it printed warnings to stderr' do\n        before do\n          result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n            warning: parser/current is loading parser/ruby21, which recognizes\n            warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n            warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n          MSG\n        end\n\n        it { should warn }\n      end\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:1:1: C: Missing top-level class documentation',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n\n      context 'and it printed warnings to stderr' do\n        before do\n          result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n            warning: parser/current is loading parser/ruby21, which recognizes\n            warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n            warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n          MSG\n        end\n\n        it { should fail_hook }\n      end\n    end\n\n    context 'when a generic error message is written to stderr' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'Could not find cookstyle in any of the sources'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/credo_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Credo do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.ex file2.exs])\n  end\n\n  context 'when credo exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when credo exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.ex:1:11: R: Modules should have a @moduledoc tag.',\n          'file2.ex:1:11: R: Modules should have a @moduledoc tag.'\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/css_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::CssLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.css file2.css])\n  end\n\n  context 'when csslint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      context 'with a line number' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.css: line 1, col 5, Warning - Use of !important'\n          ].join(\"\\n\"))\n        end\n\n        it { should warn }\n      end\n\n      context 'with no line number' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.css: Warning - Too many !important declarations (10), try to use less than 10'\n          ].join(\"\\n\"))\n        end\n\n        it { should warn }\n      end\n    end\n  end\n\n  context 'when csslint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      context 'with a line number' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.css: line 80, col 5, Error - Use of !important'\n          ].join(\"\\n\"))\n        end\n\n        it { should fail_hook }\n      end\n\n      context 'with no line number' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.css: Error - Currently no rules report a rollup error, but that may change'\n          ].join(\"\\n\"))\n        end\n\n        it { should fail_hook }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/dart_analyzer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::DartAnalyzer do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.dart file2.dart])\n  end\n\n  context 'when dartanalyzer exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when dartanalyzer exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'Analyzing file1.dart...',\n          'error • message_ommitted • lib/file1.dart:35:3 • rule',\n          'Analyzing file2.dart...',\n          'hint • message_ommitted • lib/file2.dart:100:13 • rule',\n          'info • message_ommitted • lib/file2.dart:113:16 • rule',\n          '3 lints found.'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/dogma_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Dogma do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.ex file2.exs])\n  end\n\n  context 'when dogma exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when dogma exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          '27 files, 3 errors!',\n          '',\n          '== test/support/model_case.ex ==',\n          '47: LineLength: Line length should not exceed 80 chars (was 85).',\n          '',\n          '== test/test_helper.exs ==',\n          '6: TrailingBlankLines: Blank lines detected at end of file',\n          '5: TrailingWhitespace: Trailing whitespace detected',\n          '',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/erb_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ErbLint do\n  let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.html.erb file2.html.erb])\n  end\n\n  context 'when erblint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when erblint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(<<-MSG)\nLinting 1 files with 14 linters...\n\nerb interpolation with '<%= (...).html_safe %>' in this context is never safe\nIn file: app/views/posts/show.html.erb:10\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/es_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::EsLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when eslint is unable to run' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:stderr).and_return('SyntaxError: Use of const in strict mode.')\n      result.stub(:stdout).and_return('')\n\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when eslint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js: line 1, col 0, Warning - Missing \"use strict\" statement. (strict)',\n          '',\n          '1 problem'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it doesnt count false positives error messages' do\n      before do\n        result.stub(:stdout).and_return([\n          '$ yarn eslint --quiet --format=compact /app/project/Error.ts',\n          '$ /app/project/node_modules/.bin/eslint --quiet --format=compact /app/project/Error.ts',\n          '',\n        ].join(\"\\n\"))\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when eslint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js: line 1, col 0, Error - Missing \"use strict\" statement. (strict)',\n          '',\n          '1 problem'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/execute_permissions_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ExecutePermissions do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'filename.txt' }\n\n  def make_executable_and_add(file, exec_bit)\n    if Overcommit::OS.windows?\n      `git update-index --add --chmod=#{exec_bit ? '+' : '-'}x #{file}`\n    else\n      FileUtils.chmod(exec_bit ? 0o755 : 0o644, file)\n      `git add #{file}`\n    end\n  end\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  shared_examples_for 'a file permission hook' do\n    context 'when file has execute permissions' do\n      let(:exec_bit) { true }\n\n      it { should fail_hook }\n    end\n\n    context 'when file does not have execute permissions' do\n      let(:exec_bit) { false }\n\n      it { should pass }\n    end\n  end\n\n  context 'when initial commit' do\n    around do |example|\n      repo do\n        touch staged_file\n        make_executable_and_add(staged_file, exec_bit)\n        example.run\n      end\n    end\n\n    before do\n      context.stub(:initial_commit?).and_return(true)\n    end\n\n    it_behaves_like 'a file permission hook'\n  end\n\n  context 'when not initial commit' do\n    around do |example|\n      repo do\n        `git commit --allow-empty -m \"Initial commit\"`\n        touch staged_file\n        make_executable_and_add(staged_file, exec_bit)\n        example.run\n      end\n    end\n\n    before do\n      context.stub(:initial_commit?).and_return(false)\n    end\n\n    it_behaves_like 'a file permission hook'\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/fasterer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Fasterer do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  let(:applicable_files) { %w[file1.rb file2.rb] }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(applicable_files)\n  end\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  before do\n    subject.stub(:execute).with(%w[fasterer], args: applicable_files).and_return(result)\n  end\n\n  context 'and has 2 suggestions for speed improvement' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nspec/models/product_spec.rb\nUsing each_with_index is slower than while loop. Occurred at lines: 52.\n1 files inspected, 1 offense detected\nspec/models/book_spec.rb\nUsing each_with_index is slower than while loop. Occurred at lines: 32.\n1 files inspected, 1 offense detected\nspec/models/blog_spec.rb\nUsing each_with_index is slower than while loop. Occurred at lines: 12.\n2 files inspected, 0 offense detected\n        MSG\n      )\n    end\n\n    it { should warn }\n  end\n\n  context 'and has single suggestion for speed improvement' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nspec/models/product_spec.rb\nUsing each_with_index is slower than while loop. Occurred at lines: 52.\n1 files inspected, 1 offense detected\n        MSG\n      )\n    end\n\n    it { should warn }\n  end\n\n  context 'and does not have any suggestion' do\n    let(:result) do\n      double(success?: true, stdout: '55 files inspected, 0 offenses detected')\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/file_size_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::FileSize do\n  let(:config) do\n    Overcommit::ConfigurationLoader.default_configuration.merge(\n      Overcommit::Configuration.new(\n        'PreCommit' => {\n          'FileSize' => {\n            'size_limit_bytes' => 10\n          }\n        }\n      )\n    )\n  end\n\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'filename.txt' }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add \"#{staged_file}\" > #{File::NULL} 2>&1`\n      example.run\n    end\n  end\n\n  context 'when a big file is committed' do\n    let(:contents) { 'longer than 10 bytes' }\n\n    it { should fail_hook }\n  end\n\n  context 'when a small file is committed' do\n    let(:contents) { 'short' }\n\n    it { should pass }\n  end\n\n  context 'when a file is removed' do\n    let(:contents) { 'anything' }\n    before do\n      `git commit -m \"Add file\"`\n      `git rm \"#{staged_file}\"`\n    end\n\n    it 'should not raise an exception' do\n      lambda { should pass }.should_not raise_error\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/fix_me_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::FixMe do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'filename.txt' }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when file contains FIXME' do\n    let(:contents) { 'eval(params[:q]) # FIXME maybe this is a bad idea?' }\n\n    it { should warn }\n  end\n\n  context 'when file contains TODO with special chars around it' do\n    let(:contents) { 'users = (1..1000).map { |i| User.find(1) } #TODO: make it better' }\n\n    it { should warn }\n  end\n\n  context 'when file does not contain any FixMe words' do\n    let(:contents) { 'if HACKY_CONSTANT.blank?' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/flay_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Flay do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  let(:applicable_files) { %w[file1.rb] }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(applicable_files)\n  end\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  before do\n    command = %w[flay --mass 16 --fuzzy 1]\n    subject.stub(:execute).with(command, args: applicable_files).and_return(result)\n  end\n\n  context 'flay discovered two issues' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nTotal score (lower is better) = 268\n\n1) IDENTICAL code found in :defn (mass*2 = 148)\n  app/whatever11.rb:105\n  app/whatever12.rb:76\n\n2) Similar code found in :defn (mass = 120)\n  app/whatever21.rb:105\n  app/whatever22.rb:76\n\n        MSG\n      )\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'flay discovered no issues' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nTotal score (lower is better) = 0\n        MSG\n      )\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/foodcritic_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Foodcritic do\n  let(:context) { double('context') }\n  let(:result) { double(success?: true) }\n  subject { described_class.new(config, context) }\n\n  before do\n    modified_files = applicable_files.map do |file|\n      File.join(Overcommit::Utils.repo_root, file)\n    end\n    subject.stub(:applicable_files).and_return(modified_files)\n    allow(subject).to receive(:execute).and_return(result)\n  end\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  context 'when working in a single cookbook repository' do\n    let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n\n    context 'and files have changed' do\n      let(:applicable_files) do\n        [\n          'metadata.rb',\n          File.join('recipes', 'default.rb'),\n        ]\n      end\n\n      it 'passes the repository root as the cookbook path' do\n        expect(subject).to receive(:execute).\n          with(subject.command,\n               hash_including(args: ['-B', Overcommit::Utils.repo_root]))\n        subject.run\n      end\n\n      context 'and Foodcritic returns an unsuccessful exit status' do\n        let(:result) do\n          double(\n            success?: false,\n            stderr: '',\n            stdout: <<-MSG,\n            FC023: Prefer conditional attributes: recipes/default.rb:11\n            FC065: Ensure source_url is set in metadata: metadata.rb:1\n            MSG\n          )\n        end\n\n        it { should warn }\n      end\n\n      context 'and Foodcritic returns a successful exit status' do\n        it { should pass }\n      end\n    end\n  end\n\n  context 'when working in a repository with many cookbooks' do\n    let(:config) do\n      Overcommit::ConfigurationLoader.default_configuration.merge(\n        Overcommit::Configuration.new(\n          'PreCommit' => {\n            'Foodcritic' => {\n              'cookbooks_directory' => 'cookbooks',\n              'environments_directory' => 'environments',\n              'roles_directory' => 'roles',\n            }\n          }\n        )\n      )\n    end\n\n    context 'and multiple cookbooks, environments, and roles have changed' do\n      let(:applicable_files) do\n        [\n          File.join('cookbooks', 'cookbook_a', 'metadata.rb'),\n          File.join('cookbooks', 'cookbook_b', 'metadata.rb'),\n          File.join('environments', 'production.json'),\n          File.join('environments', 'staging.json'),\n          File.join('roles', 'role_a.json'),\n          File.join('roles', 'role_b.json'),\n        ]\n      end\n\n      it 'passes the modified cookbook, environment, and role paths' do\n        expect(subject).to receive(:execute).\n          with(subject.command,\n               hash_including(args: [\n                 '-B', File.join(Overcommit::Utils.repo_root, 'cookbooks', 'cookbook_a'),\n                 '-B', File.join(Overcommit::Utils.repo_root, 'cookbooks', 'cookbook_b'),\n                 '-E', File.join(Overcommit::Utils.repo_root, 'environments', 'production.json'),\n                 '-E', File.join(Overcommit::Utils.repo_root, 'environments', 'staging.json'),\n                 '-R', File.join(Overcommit::Utils.repo_root, 'roles', 'role_a.json'),\n                 '-R', File.join(Overcommit::Utils.repo_root, 'roles', 'role_b.json'),\n               ]))\n        subject.run\n      end\n\n      context 'and Foodcritic returns an unsuccessful exit status' do\n        let(:result) do\n          double(\n            success?: false,\n            stderr: '',\n            stdout: <<-MSG,\n            FC023: Prefer conditional attributes: cookbooks/cookbook_a/recipes/default.rb:11\n            FC065: Ensure source_url is set in metadata: cookbooks/cookbook_b/metadata.rb:1\n            MSG\n          )\n        end\n\n        it { should warn }\n      end\n\n      context 'and Foodcritic returns a successful exit status' do\n        let(:result) { double(success?: true) }\n\n        it { should pass }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/forbidden_branches_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ForbiddenBranches do\n  let(:default_config) { Overcommit::ConfigurationLoader.default_configuration }\n  let(:branch_patterns) { ['master', 'release/*'] }\n  let(:config) do\n    default_config.merge(\n      Overcommit::Configuration.new(\n        'PreCommit' => {\n          'ForbiddenBranches' => {\n            'branch_patterns' => branch_patterns\n          }\n        }\n      )\n    )\n  end\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  around do |example|\n    repo do\n      `git checkout -b #{current_branch} > #{File::NULL} 2>&1`\n      example.run\n    end\n  end\n\n  context 'when committing to a permitted branch' do\n    let(:current_branch) { 'permitted' }\n    it { should pass }\n  end\n\n  context 'when committing to a forbidden branch' do\n    context 'when branch name matches a forbidden branch exactly' do\n      let(:current_branch) { 'master' }\n      it { should fail_hook }\n    end\n\n    context 'when branch name matches a forbidden branch glob pattern' do\n      let(:current_branch) { 'release/1.0' }\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/go_fmt_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::GoFmt do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:files) do\n    %w[\n      pkg1/file1.go\n      pkg1/file2.go\n      pkg2/file1.go\n      file1.go\n    ]\n  end\n  before do\n    subject.stub(:applicable_files).and_return(files)\n  end\n\n  context 'when go fmt exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it 'executes go fmt for each file' do\n      files.each do |file|\n        expect(subject).to receive(:execute).with(subject.command, args: [file]).once\n      end\n      subject.run\n    end\n\n    it 'passes' do\n      expect(subject).to pass\n    end\n  end\n\n  context 'when go fmt exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'when go fmt returns an error to stdout' do\n      let(:error_message) { 'some go fmt error' }\n\n      before do\n        result.stub(:stdout).and_return(error_message)\n        result.stub(:stderr).and_return('')\n      end\n\n      it 'executes go fmt for each file' do\n        files.each do |file|\n          expect(subject).to receive(:execute).with(subject.command, args: [file]).once\n        end\n        subject.run\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns errors' do\n        message = subject.run.last\n        expect(message).to eq Array.new(files.count, error_message).join(\"\\n\")\n      end\n    end\n\n    context 'when fo fmt returns an error to stderr' do\n      let(:error_message) { 'go: command not found' }\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return(error_message)\n      end\n\n      it 'executes go fmt for each file' do\n        files.each do |file|\n          expect(subject).to receive(:execute).with(subject.command, args: [file]).once\n        end\n        subject.run\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq Array.new(files.count, error_message).join(\"\\n\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/go_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::GoLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.go file2.go])\n  end\n\n  context 'when golint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(stdout: '', stderr: '')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports an error' do\n      context 'on stdout' do\n        before do\n          result.stub(\n            stderr: '',\n            stdout: 'file1.go:1:1: error should be the last type when returning multiple items',\n          )\n        end\n\n        it { should fail_hook }\n      end\n\n      context 'on stderr' do\n        before do\n          result.stub(\n            stdout: '',\n            stderr: \"file1.go:1:1: expected 'package', found 'IDENT' foo\"\n          )\n        end\n\n        it { should fail_hook }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/go_vet_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::GoVet do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.go file2.go])\n  end\n\n  context 'when go vet exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when go vet exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'when go tool vet is not installed' do\n      before do\n        result.stub(\n          stderr: 'go tool: no such tool \"vet\"; to install:',\n        )\n      end\n\n      it { should fail_hook /is not installed/ }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(\n          stderr: 'file1.go:1: possible formatting directive in Print call',\n        )\n      end\n\n      it { should fail_hook /formatting directive/ }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/golangci_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::GolangciLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:files) do\n    %w[\n      pkg1/file1.go\n      pkg1/file2.go\n      pkg2/file1.go\n      file1.go\n    ]\n  end\n  let(:packages) { %w[pkg1 pkg2 .] }\n  before do\n    subject.stub(:applicable_files).and_return(files)\n  end\n\n  context 'when golangci-lint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it 'passes packages to golangci-lint' do\n      expect(subject).to receive(:execute).with(subject.command, args: packages)\n      subject.run\n    end\n\n    it 'passes' do\n      expect(subject).to pass\n    end\n  end\n\n  context 'when golangci-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'when golangci-lint returns an error' do\n      let(:error_message) do\n        'pkg1/file1.go:8:6: exported type `Test` should have comment or be unexported (golint)'\n      end\n\n      before do\n        result.stub(:stdout).and_return(error_message)\n        result.stub(:stderr).and_return('')\n      end\n\n      it 'passes packages to golangci-lint' do\n        expect(subject).to receive(:execute).with(subject.command, args: packages)\n        subject.run\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message.file).to eq 'pkg1/file1.go'\n        expect(message.line).to eq 8\n        expect(message.content).to eq error_message\n      end\n    end\n\n    context 'when a generic error message is written to stderr' do\n      let(:error_message) { 'golangci-lint: command not found' }\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return(error_message)\n      end\n\n      it 'passes packages to golangci-lint' do\n        expect(subject).to receive(:execute).with(subject.command, args: packages)\n        subject.run\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq error_message\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/hadolint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Hadolint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  let(:applicable_files) { %w[Dockerfile Dockerfile.web] }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(applicable_files)\n  end\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  before do\n    subject.stub(:execute).with(%w[hadolint], args: Array(applicable_files.first)).\n      and_return(result_dockerfile)\n    subject.stub(:execute).with(%w[hadolint], args: Array(applicable_files.last)).\n      and_return(result_dockerfile_web)\n  end\n\n  context 'and has 2 suggestions' do\n    let(:result_dockerfile) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nDockerfile:5 DL3015 Avoid additional packages by specifying `--no-install-recommends`\n        MSG\n      )\n    end\n    let(:result_dockerfile_web) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nDockerfile.web:13 DL3020 Use COPY instead of ADD for files and folders\n        MSG\n      )\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'and has single suggestion for double quote' do\n    let(:result_dockerfile) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nDockerfile:11 SC2086 Double quote to prevent globbing and word splitting.\n        MSG\n      )\n    end\n    let(:result_dockerfile_web) do\n      double(success?: true, stdout: '')\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'and does not have any suggestion' do\n    let(:result_dockerfile) do\n      double(success?: true, stdout: '')\n    end\n    let(:result_dockerfile_web) do\n      double(success?: true, stdout: '')\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/haml_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::HamlLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.haml file2.haml])\n  end\n\n  context 'when haml-lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when haml-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.haml:1 [W] Prefer single quoted strings',\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.haml:1 [E] Unbalanced brackets.',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/hard_tabs_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::HardTabs do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'filename.txt' }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when file contains hard tabs' do\n    let(:contents) { \"Some\\thard\\ttabs\" }\n\n    it { should fail_hook }\n  end\n\n  context 'when file has no hard tabs' do\n    let(:contents) { 'Just some text' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/hlint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Hlint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.hs file2.hs])\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when hlint exits successfully' do\n    before do\n      result.stub(success?: true, stdout: '')\n    end\n\n    it { should pass }\n  end\n\n  context 'when hlint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.hs:22:16: Warning: Use const\n          Found:\n            \\\\ _ -> False\n          Why not:\n            const False\n        OUT\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.hs:22:5: Error: Redundant lambda\n          Found:\n            nameHack = \\\\ _ -> Nothing\n          Why not:\n            nameHack _ = Nothing\n        OUT\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/html_hint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::HtmlHint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.html file2.html])\n  end\n\n  context 'when htmlhint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.html:',\n          \"\\tline 355, col 520: \\e[31mId redefinition of [ stats ].\\e[39m\",\n          '',\n          '',\n          '1 problem.'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/html_tidy_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::HtmlTidy do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.html file2.html])\n  end\n\n  context 'when tidy exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors' do\n      before do\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when tidy exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stderr).and_return([\n          'line 4 column 24 - Warning: <html> proprietary attribute \"class\"'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n          'line 1 column 1 - Error: <foo> is not recognized!'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/image_optim_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ImageOptim do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.jpg file2.png])\n  end\n\n  context 'when image_optim exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and images were optimized' do\n      before do\n        result.stub(:stdout).and_return([\n          '32.30%   1.2K  file1.jpg',\n          'Total: 32.30%   1.2K',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and no images were optimized' do\n      before do\n        result.stub(:stdout).and_return([\n          '------         app/assets/images/favicons/favicon-96x96.png',\n          'Total: ------',\n        ].join(\"\\n\"))\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when image_optim exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('An error occurred')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/java_checkstyle_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::JavaCheckstyle do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.java file2.java])\n  end\n\n  context 'when checkstyle exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors or warnings' do\n      before do\n        result.stub(:stdout).and_return([\n          'Starting audit...',\n          'Audit done.',\n        ].join(\"\\n\"))\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when checkstyle exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a message with no severity tag' do\n      before do\n        result.stub(:stdout).and_return([\n          'Starting audit...',\n          'file1.java:1: Missing a Javadoc comment.',\n          'Audit done.'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'Starting audit...',\n          '[ERROR] file1.java:1: Missing a Javadoc comment.',\n          'Audit done.'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports an warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'Starting audit...',\n          '[WARN] file1.java:1: Missing a Javadoc comment.',\n          'Audit done.'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an info message' do\n      before do\n        result.stub(:stdout).and_return([\n          'Starting audit...',\n          '[INFO] file1.java:1: Missing a Javadoc comment.',\n          'Audit done.'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/js_hint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::JsHint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when jshint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true, stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when jshint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js: line 1, col 0, Missing semicolon. (W033)',\n          '',\n          '1 error'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js: line 1, col 0, Missing \"use strict\" statement. (E007)',\n          '',\n          '1 error'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/js_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::JsLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when jslint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when jslint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          \"file1.js:1:3: Expected ']' at column 9, not column 3.\"\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/jscs_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Jscs do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when no configuration is found' do\n    before do\n      result = double('result')\n      result.stub(success?: false,\n                  status: 4,\n                  stdout: '',\n                  stderr: 'Configuration file some-path/.jscs.json was not found.')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when jscs exits unsuccessfully with status code 2' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: false, stderr: '', status: 2)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js: line 1, col 4, ruleName: Missing space after `if` keyword'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/jsl_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Jsl do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when jsl exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when jsl exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js(1): lint warning: meaningless block; curly braces have no impact'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.js(1): SyntaxError: invalid label'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/json_syntax_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'json'\n\ndescribe Overcommit::Hook::PreCommit::JsonSyntax do\n  let(:config)      { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context)     { double('context') }\n  let(:staged_file) { 'my_file.json' }\n\n  subject           { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      touch staged_file\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when JSON files have no errors' do\n    before do\n      JSON.stub(:parse)\n    end\n\n    it { should pass }\n  end\n\n  context 'when JSON file has errors' do\n    before do\n      JSON.stub(:parse).and_raise(JSON::ParserError)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/kt_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::KtLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.kt file2.kt])\n  end\n\n  context 'when KtLint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when KtLint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.kt:12:10: error message'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/license_finder_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::LicenseFinder do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when license_finder exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when license_finder runs unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/license_header_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::LicenseHeader do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  context 'when license file is missing' do\n    it { should fail_hook }\n  end\n\n  context 'when license file exists' do\n    let(:license_contents) { <<-LICENSE }\n    Look at me\n    I'm a license\n    LICENSE\n\n    let(:file) { 'some-file.txt' }\n\n    before do\n      File.open('LICENSE.txt', 'w') { |f| f.write(license_contents) }\n      subject.stub(:applicable_files).and_return([file])\n    end\n\n    context 'when all files contain the license header' do\n      before do\n        File.open(file, 'w') do |f|\n          license_contents.split(\"\\n\").each do |line|\n            f.puts(\"// #{line}\")\n          end\n          f.write('And some text')\n        end\n      end\n\n      it { should pass }\n    end\n\n    context 'when a file is missing a license header' do\n      before do\n        File.open(file, 'w') { |f| f.write('Some text without a license') }\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/line_endings_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::LineEndings do\n  let(:config) do\n    Overcommit::ConfigurationLoader.default_configuration.merge(\n      Overcommit::Configuration.new(\n        'PreCommit' => {\n          'LineEndings' => {\n            'eol' => eol\n          }\n        }\n      )\n    )\n  end\n\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:eol) { \"\\n\" }\n  let(:staged_file) { 'filename.txt' }\n\n  before do\n    skip('Skip LineEndings tests for Git < 2.10') if Overcommit::GIT_VERSION < '2.10'\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add \"#{staged_file}\" > #{File::NULL} 2>&1`\n      example.run\n    end\n  end\n\n  context 'when file path contains spaces' do\n    let!(:staged_file) { 'a file with spaces.txt' }\n    let(:contents) { \"test\\n\" }\n\n    it { should_not fail_hook }\n  end\n\n  context 'when enforcing \\n' do\n    context 'when file contains \\r\\n line endings' do\n      let(:contents) { \"CR-LF\\r\\nline\\r\\nendings\\r\\n\" }\n\n      it { should fail_hook }\n    end\n\n    context 'when file contains \\n endings' do\n      let(:contents) { \"LF\\nline\\nendings\\n\" }\n\n      it { should pass }\n    end\n  end\n\n  context 'when enforcing \\r\\n' do\n    let(:eol) { \"\\r\\n\" }\n\n    context 'when file contains \\r\\n line endings' do\n      let(:contents) { \"CR-LF\\r\\nline\\r\\nendings\\r\\n\" }\n\n      it { should pass }\n    end\n\n    context 'when file contains \\n line endings' do\n      let(:contents) { \"LF\\nline\\nendings\\n\" }\n\n      it { should fail_hook }\n    end\n  end\n\n  unless Overcommit::OS.windows?\n    context 'when attempting to check a binary file' do\n      let(:contents) { \"\\xFF\\xD8\\xFF\\xE0\\u0000\\u0010JFIF\" }\n\n      it { should warn }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/local_paths_in_gemfile_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::LocalPathsInGemfile do\n  let(:config)      { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context)     { double('context') }\n  let(:staged_file) { 'Gemfile' }\n\n  subject           { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when file contains a local path in Ruby 1.8 hash syntax format' do\n    let(:contents) { \"gem 'fuubar', :path => '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when file contains a local path on its own line in Ruby 1.8 hash syntax format' do\n    let(:contents) { \":path => '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when file contains a local path starting with leading spaces in Ruby 1.8 hash format' do\n    let(:contents) { \" :path => '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when file contains a local path in Ruby 1.9 hash syntax format' do\n    let(:contents) { \"gem 'fuubar', path: '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when file contains local path on its own line in Ruby 1.9 hash syntax format' do\n    let(:contents) { \"path: '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when file contains local path starting with leading spaces in Ruby 1.9 hash format' do\n    let(:contents) { \" path: '../fuubar'\" }\n\n    it { should warn }\n  end\n\n  context 'when the file does not contain a local path' do\n    let(:contents) { \"gem 'fuubar'\" }\n\n    it { should pass }\n  end\n\n  context 'when the file contains local paths, but only in comments' do\n    let(:contents) do\n      [\n        \"# gem 'fuubar', :path => '../fuubar'\",\n        \"# :path => '../fuubar'\",\n        \"# gem 'fuubar', path: '../fuubar'\",\n        \"# path: '../fuubar'\",\n      ].join(\"\\n\")\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/mdl_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Mdl do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    result.stub(success?: success, stdout: stdout, stderr: stderr)\n    subject.stub(:applicable_files).and_return(%w[file1.md file2.md])\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when mdl exits successfully' do\n    let(:success) { true }\n    let(:stdout) { '' }\n    let(:stderr) { '' }\n\n    it { should pass }\n  end\n\n  context 'when mdl exits unsuccessfully' do\n    let(:success) { false }\n    let(:message) { subject.run.last }\n\n    context 'and it reports an error' do\n      let(:stdout) do\n        '[{\"filename\":\"file1.md\",\"line\":1,\"rule\":\"MD013\",\"aliases\":[\"line-length\"],'\\\n        '\"description\":\"Line length\"}]'\n      end\n      let(:stderr) { '' }\n\n      it { should fail_hook }\n      it { expect(message.file).to eq 'file1.md' }\n      it { expect(message.line).to eq 1 }\n      it { expect(message.content).to eq 'file1.md:1 MD013 Line length' }\n    end\n\n    context 'when there is an error running mdl' do\n      let(:stdout) { '' }\n      let(:stderr) { 'Some runtime error' }\n\n      it { should fail_hook }\n      it { expect(message).to eq 'Some runtime error' }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/merge_conflicts_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::MergeConflicts do\n  let(:config)      { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context)     { double('context') }\n  let(:staged_file) { 'filename.txt' }\n\n  subject           { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when file contains a merge conflict marker' do\n    let(:contents) { \"Just\\n<<<<<<< HEAD:filename.txt\\nconflicting text\" }\n\n    it { should fail_hook }\n  end\n\n  context 'when file does not have any merge conflict markers' do\n    let(:contents) { 'Just some text' }\n\n    it { should pass }\n  end\n\n  context \"when file contains characters that aren't conflict markers\" do\n    let(:contents) { 'Just some <<<<<<< arrows' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/mix_format_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::MixFormat do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.ex file2.exs])\n  end\n\n  context 'when mix format exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when mix format exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          '** (Mix) mix format failed due to --check-formatted.',\n          'The following files are not formatted:',\n          '',\n          '  * lib/file1.ex',\n          '  * lib/file2.ex'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/nginx_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::NginxTest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[nginx.conf])\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when nginx -t exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when nginx -t exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: false, stderr: normalize_indent(<<-OUT))\n        nginx: [emerg] unknown directive \"erver\" in nginx.conf:2\n        nginx: configuration file nginx.conf test failed\n      OUT\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pep257_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pep257 do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pep257 exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pep257 exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n          'file1.py:1 in public method `foo`:',\n          '        D102: Docstring missing'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pep8_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pep8 do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pep8 exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true, stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pep8 exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:1:1: W391 blank line at end of file'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:1:80: E501 line too long (80 > 79 characters)'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/php_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PhpLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(['sample.php'])\n  end\n\n  context 'when php lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:status).and_return(0)\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return('No syntax errors detected in sample.php')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when php lint exits unsuccessfully' do\n    before do\n      # php -l prints the same to both stdout and stderr\n      # rubocop:disable Layout/LineLength\n      sample_output = [\n        '',\n        \"Parse error: syntax error, unexpected '0' (T_LNUMBER), expecting variable (T_VARIABLE) or '{' or '$' in sample.php on line 3 \",\n        'Errors parsing invalid.php',\n      ].join(\"\\n\")\n      # rubocop:enable Layout/LineLength\n\n      result = double('result')\n      result.stub(:status).and_return(255)\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return(sample_output)\n      result.stub(:stderr).and_return(sample_output)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/php_stan_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PhpStan do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[sample.php])\n  end\n\n  context 'when phpstan exits successfully' do\n    before do\n      sample_output = ''\n\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return(sample_output)\n      result.stub(:status).and_return(0)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when phpstan exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      result.stub(:status).and_return(2)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        sample_output = [\n          '/sample1.php:14:Call to an undefined static method Sample1::where()',\n          '/sample2.php:17:Anonymous function has an unused use $myVariable.'\n        ].join(\"\\n\")\n        result.stub(:stdout).and_return(sample_output)\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/phpcs_fixer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PhpCsFixer do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[sample.php])\n  end\n\n  context 'when phpcs fixer exits successfully with fixed file' do\n    before do\n      # rubocop:disable Layout/LineLength\n      sample_output = [\n        'Loaded config default.',\n        'Using cache file \".php_cs.cache\".',\n        'F',\n        'Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error',\n        '   1) foo/fixable.php (braces)',\n        '',\n        'Fixed all files in 0.001 seconds, 10.000 MB memory used',\n        '',\n      ].join(\"\\n\")\n      # rubocop:enable Layout/LineLength\n\n      result = double('result')\n      result.stub(:status).and_return(0)\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return(sample_output)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\n\n  context 'when phpcs fixer exits successfully with no file to fix' do\n    before do\n      # rubocop:disable Layout/LineLength\n      sample_output = [\n        'Loaded config default.',\n        'Using cache file \".php_cs.cache\".',\n        'S',\n        'Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error',\n        '',\n      ].join(\"\\n\")\n      # rubocop:enable Layout/LineLength\n\n      result = double('result')\n      result.stub(:status).and_return(0)\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return(sample_output)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when phpcs exits unsuccessfully' do\n    before do\n      # rubocop:disable Layout/LineLength\n      sample_output = [\n        'Loaded config default.',\n        'Using cache file \".php_cs.cache\".',\n        'I',\n        'Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error',\n        'Fixed all files in 0.001 seconds, 10.000 MB memory used',\n        '',\n        'Files that were not fixed due to errors reported during linting before fixing:',\n        '   1) /home/damien/Code/Rezdy/php/foo/broken.php',\n        '',\n      ].join(\"\\n\")\n      # rubocop:enable Layout/LineLength\n\n      result = double('result')\n      result.stub(:status).and_return(1)\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return(sample_output)\n      result.stub(:stderr).and_return(sample_output)\n      subject.stub(:execute).and_return(result)\n    end\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/phpcs_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PhpCs do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[sample.php])\n  end\n\n  context 'when phpcs exits successfully' do\n    before do\n      sample_output = [\n        'File,Line,Column,Type,Message,Source,Severity,Fixable',\n        ''\n      ].join(\"\\n\")\n\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      result.stub(:stdout).and_return(sample_output)\n      result.stub(:status).and_return(0)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when phpcs exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      result.stub(:status).and_return(2)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        # rubocop:disable Layout/LineLength\n        sample_output = [\n          'File,Line,Column,Type,Message,Source,Severity,Fixable',\n          '\"/Users/craig/HelpScout/overcommit-testing/invalid.php\",5,1,warning,\"Possible parse error: FOREACH has no AS statement\",Squiz.ControlStructures.ForEachLoopDeclaration.MissingAs,5,0'\n        ].join(\"\\n\")\n        # rubocop:enable Layout/LineLength\n        result.stub(:stdout).and_return(sample_output)\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        # rubocop:disable Layout/LineLength\n        sample_output = [\n          'File,Line,Column,Type,Message,Source,Severity,Fixable',\n          '\"/Users/craig/HelpScout/overcommit-testing/invalid.php\",5,1,error,\"Inline control structures are not allowed\",Generic.ControlStructures.InlineControlStructure.NotAllowed,5,1'\n        ].join(\"\\n\")\n        # rubocop:enable Layout/LineLength\n        result.stub(:stdout).and_return(sample_output)\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pronto_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pronto do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when pronto exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pronto exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return([\n          'file2.rb:10 E: IDENTICAL code found in :iter.',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return <<~MESSAGE\n          Running Pronto::Rubocop\n          file1.rb:12 W: Line is too long. [107/80]\n          file2.rb:14 I: Prefer single-quoted strings\n\n          ```suggestion\n          x = 'x'\n          ```\n        MESSAGE\n      end\n\n      it { should warn }\n    end\n\n    context 'and it has a generic error message written to stderr' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'Could not find pronto in any of the sources'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/puppet_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PuppetLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.pp file2.pp])\n  end\n\n  context 'when puppet-lint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.pp:2:10:WARNING: selector inside resource block (selector_inside_resource)'\n        OUT\n      end\n\n      it { should warn }\n    end\n  end\n\n  context 'when puppet-lint exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.pp:2:10:WARNING: selector inside resource block (selector_inside_resource)'\n        OUT\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.pp:2:10:ERROR: trailing whitespace (trailing_whitespace)'\n        OUT\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/puppet_metadata_json_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PuppetMetadataJsonLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.pp file2.pp metadata.json])\n  end\n\n  context 'when metadata-json-lint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          (WARN) requirements: The 'pe' requirement is no longer supported by the Forge.\n        OUT\n      end\n\n      it { should warn }\n    end\n  end\n\n  context 'when metadata-json-lint exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          (WARN) requirements: The 'pe' requirement is no longer supported by the Forge.\n        OUT\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          (ERR) requirements: The 'pe' requirement is no longer supported by the Forge.\n        OUT\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pycodestyle_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pycodestyle do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pycodestyle exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true, stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pycodestyle exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:1:1: W391 blank line at end of file'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:1:80: E501 line too long (80 > 79 characters)'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pydocstyle_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pydocstyle do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pydocstyle exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pydocstyle exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n          'file1.py:1 in public method `foo`:',\n          '        D102: Docstring missing'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pyflakes_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pyflakes do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pyflakes exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pyflakes exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: false, stdout: '', stderr: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          \"file1.py:1: local variable 'x' is assigned to but never used\"\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n          'file1.py:1:1: invalid syntax'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/pylint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Pylint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when pylint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pylint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:2:C: Missing function docstring (missing-docstring)'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          \"file1.py:2:E: Instance of 'Foo' has no 'bar' member (no-member)\"\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/python_flake8_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::PythonFlake8 do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.py file2.py])\n  end\n\n  context 'when flake8 exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when flake8 exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:1:1: W292 no newline at end of file'\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.py:2:13: F812 list comprehension redefines name from line 1'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/r_spec_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RSpec do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when rspec exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n\n    it {\n      expect(subject).to receive(:execute).with(['rspec']).and_return(result)\n\n      subject.run\n    }\n  end\n\n  context 'with included files set' do\n    let(:result) { double('result') }\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'PreCommit' => {\n                        'RSpec' => {\n                          'include' => ['**/*_spec.rb'],\n                        }\n                      }\n      ))\n    end\n\n    let(:context) { double('context') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n      subject.stub(:applicable_files).and_return('spec/test_spec.rb')\n    end\n\n    it { should pass }\n\n    it {\n      expect(subject).to receive(:execute).with(['rspec'],\n                                                args: 'spec/test_spec.rb').and_return(result)\n\n      subject.run\n    }\n  end\n\n  context 'when rspec exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `load': /home/user/dev/github/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:49: can't find string \"EOS\" anywhere before EOF (SyntaxError)\n          /home/user/dev/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:29: syntax error, unexpected end-of-input\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `block in load_spec_files'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `each'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `load_spec_files'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:97:in `setup'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:85:in `run'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:70:in `run'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:38:in `invoke'\n            from /home/user/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.2/exe/rspec:4:in `<top (required)>'\n            from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `load'\n            from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `<main>'\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'with a test failure' do\n      before do\n        result.stub(stderr: '', stdout: <<-MSG)\n          .FF\n\n          Failures:\n\n            1) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail\n               Failure/Error: it { should fail_hook }\n                 expected that the hook would fail\n               # ./spec/overcommit/hook/pre_push/rspec_spec.rb:45:in `block (4 levels) in <top (required)>'\n\n            2) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail\n               Failure/Error: it { should fail_hook }\n                 expected that the hook would fail\n               # ./spec/overcommit/hook/pre_push/rspec_spec.rb:57:in `block (4 levels) in <top (required)>'\n\n          Finished in 0.00505 seconds (files took 0.27437 seconds to load)\n          3 examples, 2 failures\n\n          Failed examples:\n\n          rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:45 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail\n          rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:57 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/rails_best_practices_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RailsBestPractices do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when rails_best_practices exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when rails_best_practices exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:7 - simplify render in controllers',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'when there is an error running rails_best_practices' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'Something went wrong with rails_best_practices'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/rails_schema_up_to_date_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RailsSchemaUpToDate do\n  let(:config)            { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context)           { double('context') }\n  let(:ruby_schema_file)  { 'db/schema.rb' }\n  let(:sql_schema_file)   { 'db/structure.sql' }\n\n  let(:migration_files) do\n    %w[\n      db/migrate/20140304042233_some_migration.rb\n      db/migrate/20140305123456_some_migration.rb\n    ]\n  end\n\n  subject { described_class.new(config, context) }\n\n  context \"when a migration was added but the schema wasn't updated\" do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when a Ruby schema file was added but no migration files were' do\n    before do\n      subject.stub(:applicable_files).and_return([ruby_schema_file])\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n        File.open(ruby_schema_file, 'w') { |f| f.write('version: 20160904205635') }\n        `git add #{ruby_schema_file}`\n        example.run\n      end\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when a SQL schema file was added but no migration files were' do\n    before do\n      subject.stub(:applicable_files).and_return([sql_schema_file])\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n        File.open(sql_schema_file, 'w') { |f| f.write(\"VALUES ('20151214213046')\") }\n        `git add #{sql_schema_file}`\n        example.run\n      end\n    end\n\n    it { should fail_hook }\n\n    context 'when non ASCII encoding is required' do\n      let!(:config) do\n        super().merge(Overcommit::Configuration.new(\n                        'PreCommit' => {\n                          'RailsSchemaUpToDate' => {\n                            'encoding' => 'utf-8'\n                          }\n                        }\n                      ))\n      end\n\n      before do\n        subject.stub(:applicable_files).and_return([sql_schema_file])\n      end\n\n      around do |example|\n        repo do\n          FileUtils.mkdir_p('db/migrate')\n          File.open(sql_schema_file, 'w') { |f| f.write(\"version: 12345678901234\\nVALUES ('字')\") }\n          `git add #{sql_schema_file}`\n          example.run\n        end\n      end\n\n      it { should fail_hook }\n    end\n  end\n\n  context 'when a Ruby schema file with the latest version and migrations are added' do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files << ruby_schema_file)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        File.open(ruby_schema_file, 'w') { |f| f.write('20140305123456') }\n        `git add #{ruby_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when a Ruby schema generated by Rails 5.2+ format and migrations are added' do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files << ruby_schema_file)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        File.open(ruby_schema_file, 'w') { |f| f.write('2014_03_05_123456') }\n        `git add #{ruby_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when a Ruby schema file which is not at the latest version and migrations are added' do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files << ruby_schema_file)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        File.open(ruby_schema_file, 'w') { |f| f.write('20140205123456') }\n        `git add #{ruby_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when a SQL schema file with the latest version and migrations are added' do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files << sql_schema_file)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        File.open(sql_schema_file, 'w') { |f| f.write('20140305123456') }\n        `git add #{sql_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when schema file w/ latest version and migrations are added in dir w/ number' do\n    let(:user_dir) { 'hdd418/' }\n    before do\n      files = (migration_files << sql_schema_file).map do |file|\n        \"#{user_dir}#{file}\"\n      end\n      subject.stub(:applicable_files).and_return(files)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('hdd418/db/migrate')\n\n        File.open(user_dir + sql_schema_file, 'w') { |f| f.write('20140305123456') }\n        `git add #{user_dir}#{sql_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(user_dir + migration_file, 'w') { |f| f.write('migration') }\n          `git add #{user_dir}#{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when a SQL schema file which is not at the latest version and migrations are added' do\n    before do\n      subject.stub(:applicable_files).and_return(migration_files << sql_schema_file)\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db/migrate')\n\n        File.open(sql_schema_file, 'w') { |f| f.write('20140205123456') }\n        `git add #{sql_schema_file}`\n\n        migration_files.each do |migration_file|\n          File.open(migration_file, 'w') { |f| f.write('migration') }\n          `git add #{migration_file}`\n        end\n\n        example.run\n      end\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'when the schema file is at version 0 and there are no migrations' do\n    before do\n      subject.stub(:applicable_files).and_return([ruby_schema_file])\n    end\n\n    around do |example|\n      repo do\n        FileUtils.mkdir_p('db')\n\n        File.open(ruby_schema_file, 'w') { |f| f.write('version: 0') }\n        `git add #{ruby_schema_file}`\n\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/rake_target_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RakeTarget do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'without targets parameters' do\n    let(:result) { double('result') }\n    it 'raises' do\n      expect { subject.run }.to raise_error(\n        RuntimeError, /RakeTarget: targets parameter is empty.*/\n      )\n    end\n  end\n\n  context 'with targets parameter set' do\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'PreCommit' => {\n                        'RakeTarget' => {\n                          'targets' => ['test'],\n                        }\n                      }\n      ))\n    end\n    let(:result) { double('result') }\n\n    context 'when rake exits successfully' do\n      before do\n        result.stub(:success?).and_return(true)\n        subject.stub(:execute).and_return(result)\n        result.stub(:stdout).and_return('ANYTHING')\n      end\n\n      it { should pass }\n    end\n\n    context 'when rake exits unsuccessfully' do\n      before do\n        result.stub(:success?).and_return(false)\n        subject.stub(:execute).and_return(result)\n        result.stub(:stdout).and_return('ANYTHING')\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/reek_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Reek do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when reek exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when reek exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports warnings' do\n      context 'in old format' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.rb -- 1 warning:',\n            'file1.rb:1: MyClass#my_method performs a nil-check. (NilCheck)'\n          ].join(\"\\n\"))\n          result.stub(:stderr).and_return('')\n        end\n\n        it { should fail_hook }\n\n        it 'parses the right file' do\n          subject.run.map(&:file).should == ['file1.rb']\n        end\n      end\n\n      context 'in new format' do\n        before do\n          result.stub(:stdout).and_return([\n            'file1.rb -- 1 warning:',\n            '  file1.rb:1: MyClass#my_method performs a nil-check. (NilCheck)'\n          ].join(\"\\n\"))\n          result.stub(:stderr).and_return('')\n        end\n\n        it { should fail_hook }\n\n        it 'parses the right file' do\n          subject.run.map(&:file).should == ['file1.rb']\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/rst_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RstLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    result.stub(success?: success, stdout: stdout, stderr: stderr)\n    subject.stub(:applicable_files).and_return(%w[file1.rst file2.rst])\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when rst-lint exits successfully' do\n    let(:success) { true }\n    let(:stdout) { '' }\n    let(:stderr) { '' }\n\n    it { should pass }\n  end\n\n  context 'when rst-lint exits unsuccessfully' do\n    let(:success) { false }\n\n    context 'and it reports an error' do\n      let(:stdout) { 'WARNING file1.rst:7 Title underline too short.' }\n      let(:stderr) { '' }\n\n      it { should fail_hook }\n    end\n\n    context 'when there is an error running rst-lint' do\n      let(:stdout) { '' }\n      let(:stderr) { 'Some runtime error' }\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/rubo_cop_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RuboCop do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when rubocop exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n\n    context 'and it printed warnings to stderr' do\n      before do\n        result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n          warning: parser/current is loading parser/ruby21, which recognizes\n          warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n          warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n        MSG\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when rubocop exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:1:1: W: Useless assignment to variable - my_var.',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should warn }\n\n      context 'and it printed warnings to stderr' do\n        before do\n          result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n            warning: parser/current is loading parser/ruby21, which recognizes\n            warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n            warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n          MSG\n        end\n\n        it { should warn }\n      end\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:1:1: C: Missing top-level class documentation',\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n\n      context 'and it printed warnings to stderr' do\n        before do\n          result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n            warning: parser/current is loading parser/ruby21, which recognizes\n            warning: 2.1.8-compliant syntax, but you are running 2.1.1.\n            warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.\n          MSG\n        end\n\n        it { should fail_hook }\n      end\n    end\n\n    context 'when a generic error message is written to stderr' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'Could not find rubocop in any of the sources'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/ruby_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RubyLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when ruby-lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when ruby-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:W:1:1: unused argument foo',\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.rb:E:1:1: undefined constant Foo',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/ruby_syntax_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::RubySyntax do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when ruby_syntax exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors' do\n      before do\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when ruby_syntax exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n                                          \"file1.rb:2: syntax error, unexpected '^'\"\n                                        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/scalariform_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Scalariform do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.scala file2.scala])\n  end\n\n  context 'when there were no failures or errors' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n      result.stub(:stdout).and_return([\n        'Assuming source is Scala 2.10.4',\n        'Formatting with default preferences.',\n        '[OK]     file1.scala',\n        '[OK]     file2.scala'\n      ].join(\"\\n\"))\n    end\n\n    it { should pass }\n  end\n\n  context 'when there were failures' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n      result.stub(:stdout).and_return([\n        'Assuming source is Scala 2.10.4',\n        'Formatting with default preferences.',\n        '[OK]     file1.scala',\n        '[FAILED] file2.scala'\n      ].join(\"\\n\"))\n    end\n\n    it { should warn }\n  end\n\n  context 'when there were errors' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n      result.stub(:stdout).and_return([\n        'Assuming source is Scala 2.10.4',\n        'Formatting with default preferences.',\n        '[ERROR]  file1.scala',\n        '[OK]     file2.scala'\n      ].join(\"\\n\"))\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/scalastyle_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Scalastyle do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.scala file2.scala])\n  end\n\n  context 'when scalastyle exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors or warnings' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          Processed 1 file(s)\n          Found 0 errors\n          Found 0 warnings\n          Finished in 490 ms\n        OUT\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          warning file=file1.scala message=Use : Unit = for procedures line=1 column=15\n          Processed 1 file(s)\n          Found 0 errors\n          Found 1 warnings\n          Finished in 490 ms\n        OUT\n      end\n\n      it { should warn(/Use : Unit = for procedures/) }\n    end\n\n    context 'and it reports a warning with no line' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          warning file=file1.scala message=File must end with newline character\n          Processed 1 file(s)\n          Found 0 errors\n          Found 1 warnings\n          Finished in 490 ms\n        OUT\n      end\n\n      it { should warn(/File must end with newline character/) }\n    end\n  end\n\n  context 'when scalastyle exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          error file=file1.scala message=Use : Unit = for procedures line=1 column=15\n          Processed 1 file(s)\n          Found 1 errors\n          Found 0 warnings\n          Finished in 490 ms\n        OUT\n      end\n\n      it { should fail_hook(/Use : Unit = for procedures/) }\n    end\n\n    context 'and it reports an error with no line' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          error file=file1.scala message=File must end with newline character\n          Processed 1 file(s)\n          Found 1 errors\n          Found 0 warnings\n          Finished in 490 ms\n        OUT\n      end\n\n      it { should fail_hook(/File must end with newline character/) }\n    end\n\n    context 'with a usage message' do\n      before do\n        result.stub(stderr: '', stdout: normalize_indent(<<-OUT))\n          scalastyle 0.7.0\n          Usage: scalastyle [options] <source directory>\n           -c, --config FILE               configuration file (required)\n           -v, --verbose true|false        verbose output\n           -q, --quiet true|false          be quiet\n               --xmlOutput FILE            write checkstyle format output to this file\n               --xmlEncoding STRING        encoding to use for the xml file\n               --inputEncoding STRING      encoding for the source files\n           -w, --warnings true|false       fail if there are warnings\n           -e, --externalJar FILE          jar containing custom rules\n        OUT\n      end\n\n      it { should fail_hook(/Usage/) }\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: normalize_indent(<<-ERR))\n          Exception in thread \"main\" java.io.FileNotFoundException: scalastyle-config.xml (No such file or directory)\n                  at java.io.FileInputStream.open0(Native Method)\n                  at java.io.FileInputStream.open(FileInputStream.java:195)\n                  at java.io.FileInputStream.<init>(FileInputStream.java:138)\n                  at java.io.FileInputStream.<init>(FileInputStream.java:93)\n                  at scala.xml.Source$.fromFile(XML.scala:22)\n                  at scala.xml.factory.XMLLoader$class.loadFile(XMLLoader.scala:50)\n                  at scala.xml.XML$.loadFile(XML.scala:60)\n                  at org.scalastyle.ScalastyleConfiguration$.readFromXml(ScalastyleConfiguration.scala:87)\n                  at org.scalastyle.Main$.execute(Main.scala:106)\n                  at org.scalastyle.Main$.main(Main.scala:95)\n                  at org.scalastyle.Main.main(Main.scala)\n        ERR\n      end\n\n      it { should fail_hook(/Exception/) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/scss_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ScssLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.scss file2.scss])\n  end\n\n  context 'when scss-lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:status).and_return(0)\n      result.stub(:stdout).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when scss-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:status).and_return(1)\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return(<<-JSON)\n          {\n            \"test.scss\": [\n              {\n                \"line\": 1,\n                \"column\": 1,\n                \"length\": 2,\n                \"severity\": \"warning\",\n                \"reason\": \"Empty rule\",\n                \"linter\": \"EmptyRule\"\n              }\n            ]\n          }\n        JSON\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:status).and_return(2)\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return(<<-JSON)\n          {\n            \"test.scss\": [\n              {\n                \"line\": 1,\n                \"column\": 1,\n                \"length\": 2,\n                \"severity\": \"error\",\n                \"reason\": \"Syntax error\",\n              }\n            ]\n          }\n        JSON\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it returns invalid JSON' do\n      before do\n        result.stub(:status).and_return(1)\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return('This is not JSON')\n      end\n\n      it { should fail_hook /Unable to parse JSON returned by SCSS-Lint/ }\n    end\n\n    context 'and it returns status code indicating all files were filtered' do\n      before do\n        result.stub(:status).and_return(81)\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/semi_standard_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::SemiStandard do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when semistandard exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true, stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when semistandard exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'semistandard: Use Semicolons For All! (https://github.com/Flet/semistandard)',\n          '  file1.js:1:1: Extra semicolon. (eslint/semi)'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/shell_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::ShellCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.sh file2.sh])\n  end\n\n  context 'when shellcheck exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when shellcheck exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a note' do\n      before do\n        result.stub(:stdout).and_return([\n          \"file1.sh:1:1: note: Use ./*.ogg so names with dashes won't become \\\n           options. [SC2035]\",\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          \"file1.sh:1:1: warning: Quote the parameter to -name so the shell \\\n           won't interpret it. [SC2061]\",\n        ].join(\"\\n\"))\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/slim_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::SlimLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.slim file2.slim])\n  end\n\n  context 'when slim-lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when slim-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.slim:1 [W] Prefer single quoted strings',\n        ].join(\"\\n\"))\n      end\n\n      it { should warn }\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.slim:1 [E] Unbalanced brackets',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/solargraph_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Solargraph do\n  let(:config) do\n    Overcommit::ConfigurationLoader.default_configuration.merge(\n      Overcommit::Configuration.new(\n        'PreCommit' => {\n          'Solargraph' => {\n            'problem_on_unmodified_line' => problem_on_unmodified_line\n          }\n        }\n      )\n    )\n  end\n  let(:problem_on_unmodified_line) { 'ignore' }\n  let(:context) { double('context') }\n  let(:messages) { subject.run }\n  let(:result) { double('result') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n    result.stub(:stderr).and_return(stderr)\n    result.stub(:stdout).and_return(stdout)\n  end\n\n  context 'when Solargraph exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it printed a message to stderr' do\n      let(:stderr) { 'stderr unexpected message that must be fine since command successful' }\n      let(:stdout) { '' }\n      it { should pass }\n    end\n\n    context 'and it printed a message to stdout' do\n      let(:stderr) { '' }\n      let(:stdout) { 'stdout message that must be fine since command successful' }\n      it { should pass }\n    end\n  end\n\n  context 'when Solargraph exits unsucessfully' do\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports typechecking issues' do\n      let(:stdout) do\n        normalize_indent(<<-MSG)\n          /home/username/src/solargraph-rails/file1.rb:36 - Unresolved constant Solargraph::Parser::Legacy::NodeChainer\n          /home/username/src/solargraph-rails/file2.rb:44 - Unresolved call to []\n          /home/username/src/solargraph-rails/file2.rb:99 - Unresolved call to []\n          Typecheck finished in 8.921023999806494 seconds.\n          189 problems found in 14 of 16 files.\n        MSG\n      end\n\n      ['', 'unexpected output'].each do |stderr_string|\n        context \"with stderr output of #{stderr_string.inspect}\" do\n          let(:stderr) { stderr_string }\n\n          it { should fail_hook }\n          it 'reports only three errors and assumes stderr is harmless' do\n            expect(messages.size).to eq 3\n          end\n          it 'parses filename' do\n            expect(messages.first.file).to eq '/home/username/src/solargraph-rails/file1.rb'\n          end\n          it 'parses line number of messages' do\n            expect(messages.first.line).to eq 36\n          end\n          it 'parses and returns error message content' do\n            msg = '/home/username/src/solargraph-rails/file1.rb:36 - Unresolved constant Solargraph::Parser::Legacy::NodeChainer'\n            expect(messages.first.content).to eq msg\n          end\n        end\n      end\n    end\n\n    context 'but it reports no typechecking issues' do\n      let(:stdout) do\n        normalize_indent(<<-MSG)\n           Typecheck finished in 8.095239999704063 seconds.\n           0 problems found in 0 of 16 files.\n        MSG\n      end\n\n      context 'with no stderr output' do\n        let(:stderr) { '' }\n        it 'should return no messages' do\n          expect(messages).to eq([:fail, 'Solargraph failed to run'])\n        end\n      end\n\n      context 'with stderr output' do\n        let(:stderr) { 'something' }\n        it 'should raise' do\n          expect { messages }.to raise_error(Overcommit::Exceptions::MessageProcessingError)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/sorbet_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Sorbet do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when Sorbet exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n\n    context 'and it printed a message to stderr' do\n      before do\n        result.stub(:stderr).and_return(\"No errors! Great job.\\n\")\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when Sorbet exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return(normalize_indent(<<-MSG))\n            sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003\n            5 |  foo 'bar'\n                      ^^^\n            Errors: 1\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/sqlint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Sqlint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.sql file2.sql])\n  end\n\n  context 'when sqlint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.sql:2:10:WARNING some warning\n        OUT\n      end\n\n      it { should warn }\n    end\n  end\n\n  context 'when sqlint exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(normalize_indent(<<-OUT))\n          file1.sql:2:10:ERROR syntax error at or near \"USE\"\n        OUT\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/standard_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Standard do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.js file2.js])\n  end\n\n  context 'when standard exits successfully' do\n    before do\n      result = double('result')\n      result.stub(success?: true, stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when standard exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'standard: Use JavaScript Standard Style (https://github.com/feross/standard)',\n          '  file1.js:1:1: Extra semicolon. (eslint/semi)'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/stylelint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Stylelint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.scss file2.scss])\n  end\n\n  context 'when stylelint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      result.stub(:stderr).and_return('')\n      result.stub(:stdout).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when stylelint exits unsucessfully with messages on stdout (stylelint < 16)' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:success?).and_return(false)\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return([\n          'index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)',\n          'form.css: line 10, col 6, error - Expected indentation of 4 spaces (indentation)',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n\n      it 'extracts lines numbers correctly from output' do\n        expect(subject.run.map(&:line)).to eq([4, 10])\n      end\n    end\n  end\n\n  context 'when stylelint exits unsucessfully with messages on stderr (stylelint >= 16)' do\n    let(:result) { double('result') }\n\n    before do\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:success?).and_return(false)\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'index.css: line 4, col 4, error - Expected indentation of 2 spaces (indentation)',\n          'form.css: line 10, col 6, error - Expected indentation of 4 spaces (indentation)',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n\n      it 'extracts lines numbers correctly from output' do\n        expect(subject.run.map(&:line)).to eq([4, 10])\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/swift_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::SwiftLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.swift file2.swift])\n  end\n\n  context 'when SwiftLint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when SwiftLint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return([\n          'file1.swift:12: warning: message: details'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/terraform_format_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::TerraformFormat do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.tf file2.tf])\n  end\n\n  context 'when Terraform exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when Terraform exits unsucessfully' do\n    let(:result_ok) { double('result') }\n    let(:result_bad) { double('result') }\n    let(:cmdline) { %w[terraform fmt -check=true -diff=false] }\n\n    before do\n      result_ok.stub(:success?).and_return(true)\n      result_bad.stub(:success?).and_return(false)\n      subject.stub(:execute).with(cmdline, args: ['file1.tf']).and_return(result_ok)\n      subject.stub(:execute).with(cmdline, args: ['file2.tf']).and_return(result_bad)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/trailing_whitespace_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::TrailingWhitespace do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'filename.txt' }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  around do |example|\n    repo do\n      File.open(staged_file, 'w') { |f| f.write(contents) }\n      `git add #{staged_file}`\n      example.run\n    end\n  end\n\n  context 'when file contains trailing whitespace' do\n    let(:contents) { 'Some trailing whitespace   ' }\n\n    it { should fail_hook }\n  end\n\n  context 'when file contains trailing tabs' do\n    let(:contents) { \"Some trailing tabs\\t\\t\" }\n\n    it { should fail_hook }\n  end\n\n  context 'when file has no invalid whitespace' do\n    let(:contents) { 'Just some text' }\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/travis_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::TravisLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(['.travis.yml'])\n  end\n\n  context 'when travis-lint exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when travis-lint exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/ts_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::TsLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[anotherfile.ts])\n  end\n\n  context 'when tslint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no output' do\n      before do\n        result.stub(:stdout).and_return('')\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when tslint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(\n          'src/file/anotherfile.ts[298, 1]: exceeds maximum line length of 140'\n        )\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports an error with an \"ERROR\" severity' do\n      before do\n        result.stub(:stdout).and_return(\n          'ERROR: src/AccountController.ts[4, 28]: expected call-signature to have a typedef'\n        )\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports an warning' do\n      before do\n        result.stub(:stdout).and_return(\n          'WARNING: src/AccountController.ts[4, 28]: expected call-signature to have a typedef'\n        )\n      end\n\n      it { should warn }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/vint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::Vint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:result) { double('result') }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.vim file2.vim])\n    subject.stub(:execute).and_return(result)\n  end\n\n  context 'when vint exits successfully' do\n    before do\n      result.stub(:success?).and_return(true)\n    end\n\n    it { should pass }\n  end\n\n  context 'when vint exits unsucessfully' do\n    before do\n      result.stub(:success?).and_return(false)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(stderr: '', stdout: [\n          'file1.vim:1:0: autocmd should execute in augroup or execute with a group',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(:stderr).and_return([\n          'vint ERROR: no such file or directory: `foo.vim`'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/w3c_css_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::W3cCss do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:fake_exception) { Class.new(StandardError) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.css file2.css])\n    stub_const('W3CValidators::ValidatorUnavailable', fake_exception)\n    stub_const('W3CValidators::ParsingError', fake_exception)\n  end\n\n  context 'when w3c_validators exits with an exception' do\n    let(:validator) { double('validator') }\n\n    before do\n      subject.stub(:validator).and_return(validator)\n    end\n\n    context 'when the validator is not available' do\n      before do\n        validator.stub(:validate_file).and_raise(W3CValidators::ValidatorUnavailable)\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'when the validator response cannot be parsed' do\n      before do\n        validator.stub(:validate_file).and_raise(W3CValidators::ParsingError)\n      end\n\n      it { should fail_hook }\n    end\n  end\n\n  context 'when w3c_validators exits without an exception' do\n    let(:validator) { double('validator') }\n    let(:results) { double('results') }\n    let(:message) { double('message') }\n\n    before do\n      validator.stub(:validate_file).and_return(results)\n      subject.stub(:validator).and_return(validator)\n    end\n\n    context 'with no errors or warnings' do\n      before do\n        results.stub(errors: [], warnings: [])\n      end\n\n      it { should pass }\n    end\n\n    context 'with a warning' do\n      before do\n        message.stub(type: :warning, line: '1', message: '')\n        results.stub(errors: [], warnings: [message])\n      end\n\n      it { should warn }\n    end\n\n    context 'with an error' do\n      before do\n        message.stub(type: :error, line: '1', message: '')\n        results.stub(errors: [message], warnings: [])\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/w3c_html_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::W3cHtml do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:fake_exception) { Class.new(StandardError) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.html file2.html])\n    stub_const('W3CValidators::ValidatorUnavailable', fake_exception)\n    stub_const('W3CValidators::ParsingError', fake_exception)\n  end\n\n  context 'when w3c_validators exits with an exception' do\n    let(:validator) { double('validator') }\n\n    before do\n      subject.stub(:validator).and_return(validator)\n    end\n\n    context 'when the validator is not available' do\n      before do\n        validator.stub(:validate_file).and_raise(W3CValidators::ValidatorUnavailable)\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'when the validator response cannot be parsed' do\n      before do\n        validator.stub(:validate_file).and_raise(W3CValidators::ParsingError)\n      end\n\n      it { should fail_hook }\n    end\n  end\n\n  context 'when w3c_validators exits without an exception' do\n    let(:validator) { double('validator') }\n    let(:results) { double('results') }\n    let(:message) { double('message') }\n\n    before do\n      validator.stub(:validate_file).and_return(results)\n      subject.stub(:validator).and_return(validator)\n    end\n\n    context 'with no errors or warnings' do\n      before do\n        results.stub(errors: [], warnings: [])\n      end\n\n      it { should pass }\n    end\n\n    context 'with a warning' do\n      before do\n        message.stub(type: :warning, line: '1', message: '')\n        results.stub(errors: [], warnings: [message])\n      end\n\n      it { should warn }\n    end\n\n    context 'with an error' do\n      before do\n        message.stub(type: :error, line: '1', message: '')\n        results.stub(errors: [message], warnings: [])\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/xml_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::XmlLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.xml file2.xml])\n  end\n\n  context 'when xmllint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with no errors' do\n      before do\n        result.stub(:stderr).and_return('')\n      end\n\n      it { should pass }\n    end\n  end\n\n  context 'when xmllint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return([\n          \"file1.xml:1: parser error : expected '='\"\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/xml_syntax_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'rexml/document'\n\ndescribe Overcommit::Hook::PreCommit::XmlSyntax do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'file1.xml' }\n\n  before do\n    IO.stub(:read).with(staged_file)\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  context 'when XML files have no errors' do\n    before do\n      REXML::Document.stub(:new)\n    end\n\n    it { should pass }\n  end\n\n  context 'when XML file has errors' do\n    before do\n      REXML::Document.stub(:new).and_raise(REXML::ParseException.new(''))\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/yaml_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::YamlLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  let(:applicable_files) { %w[file1.yaml file2.yml] }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(applicable_files)\n  end\n\n  around do |example|\n    repo do\n      example.run\n    end\n  end\n\n  before do\n    subject.stub(:execute).with(%w[yamllint --format=parsable --strict], args: applicable_files).\n      and_return(result)\n  end\n\n  context 'and has 2 suggestions for line length' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nfile1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length)\nfile2.yml:41:81: [error] line too long (261 > 80 characters) (line-length)\n        MSG\n      )\n    end\n\n    it { should fail_hook }\n  end\n\n  context 'and has 1 error and 1 warning' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nfile1.yaml:3:81: [error] line too long (253 > 80 characters) (line-length)\nfile2.yml:41:81: [warning] missing document start \"---\" (document-start)\n        MSG\n      )\n    end\n\n    it { should fail_hook }\n  end\n  context 'and has single suggestion for missing file header' do\n    let(:result) do\n      double(\n        success?: false,\n        stdout: <<-MSG\nfile1.yaml:1:1: [warning] missing document start \"---\" (document-start)\n        MSG\n      )\n    end\n\n    it { should warn }\n  end\n\n  context 'and does not have any suggestion' do\n    let(:result) do\n      double(success?: true, stdout: '')\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/yaml_syntax_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::YamlSyntax do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n  let(:staged_file) { 'file1.yml' }\n\n  before do\n    subject.stub(:applicable_files).and_return([staged_file])\n  end\n\n  context 'when YAML files have no errors' do\n    before do\n      YAML.stub(:load_file)\n    end\n\n    it { should pass }\n  end\n\n  context 'when YAML file has errors' do\n    before do\n      YAML.stub(:load_file).with(staged_file, { aliases: true }).and_raise(ArgumentError)\n      YAML.stub(:load_file).with(staged_file).and_raise(ArgumentError)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/yard_coverage_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::YardCoverage do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when yard exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return(\n        <<-HEREDOC\n      Files:          72\n      Modules:        12 (    0 undocumented)\n      Classes:        63 (    0 undocumented)\n      Constants:      91 (    0 undocumented)\n      Attributes:     11 (    0 undocumented)\n      Methods:       264 (    0 undocumented)\n      100.0% documented\n      HEREDOC\n      )\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when somehow yard exits a non-stats output' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return(\n        <<-HEREDOC\n        WHATEVER OUTPUT THAT IS NOT YARD STATS ONE\n      HEREDOC\n      )\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\n\n  context 'when somehow yard coverage is not a valid value' do\n    before do\n      result = double('result')\n      result.stub(:stdout).and_return(\n        <<-HEREDOC\n      Files:          72\n      Modules:        12 (    0 undocumented)\n      Classes:        63 (    0 undocumented)\n      Constants:      91 (    0 undocumented)\n      Attributes:     11 (    0 undocumented)\n      Methods:       264 (    0 undocumented)\n      AAAAAA documented\n      HEREDOC\n      )\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should warn }\n  end\n\n  context 'when yard exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stdout).and_return(\n          <<-HEREDOC\n        Files:          72\n        Modules:        12 (    3 undocumented)\n        Classes:        63 (   15 undocumented)\n        Constants:      91 (   79 undocumented)\n        Attributes:     11 (    0 undocumented)\n        Methods:       264 (   55 undocumented)\n        65.53% documented\n\n        Undocumented Objects:\n        ApplicationCable                                                  (app/channels/application_cable/channel.rb:1)\n        ApplicationCable::Channel                                         (app/channels/application_cable/channel.rb:2)\n        ApplicationCable::Connection                                      (app/channels/application_cable/connection.rb:2)\n        HEREDOC\n        )\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_commit/yarn_check_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreCommit::YarnCheck do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when yarn.lock is ignored' do\n    around do |example|\n      repo do\n        touch 'yarn.lock'\n        echo('yarn.lock', '.gitignore')\n        `git add .gitignore`\n        `git commit -m \"Ignore yarn.lock\"`\n        example.run\n      end\n    end\n\n    it { should pass }\n  end\n\n  context 'when yarn.lock is not ignored' do\n    let(:result) { double('result') }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      result.stub(stderr: stderr)\n      subject.stub(:execute).with(%w[git ls-files -o -i --exclude-standard]).\n        and_return(double(stdout: ''))\n      subject.stub(:execute).with(%w[yarn check --silent --no-progress --non-interactive]).\n        and_return(result)\n    end\n\n    context 'and yarn check reports no errors' do\n      let(:stderr) { '' }\n\n      it { should pass }\n\n      context 'and there was a change to the yarn.lock' do\n        before do\n          subject.stub(:execute).with(%w[yarn check --silent --no-progress --non-interactive]) do\n            echo('stuff', 'yarn.lock')\n            double(stderr: '')\n          end\n        end\n\n        it { should fail_hook }\n      end\n    end\n\n    context 'and yarn check contains only warnings' do\n      let(:stderr) do\n        <<STDERR\nwarning \"parent-package#child-package@version\" could be deduped from \"one version\" to \"another version\"\nSTDERR\n      end\n\n      it { should pass }\n    end\n\n    context 'and yarn check contains unactionable errors' do\n      let(:stderr) do\n        <<STDERR\nerror \"peer-dependency#peer@a || list || of || versions\" doesn't satisfy found match of \"peer@different-version\"\nerror \"bad-maintainer#bad-package\" is wrong version: expected \"something normal\", got \"something crazy\"\nSTDERR\n      end\n\n      it { should pass }\n    end\n\n    context 'and yarn check contains actionable errors' do\n      let(:stderr) do\n        <<STDERR\nerror Lockfile does not contain pattern: \"thing-i-updated-in-package.json@new-version\"\nSTDERR\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::Base do\n  let(:remote_name) { 'origin' }\n  let(:remote_ref_deletion?) { false }\n  let(:config) { double('config') }\n  let(:context) { double('context') }\n  let(:hook) { described_class.new(config, context) }\n\n  describe '#run?' do\n    let(:hook_config) { {} }\n\n    before do\n      allow(context).to receive(:remote_name).and_return(remote_name)\n      allow(context).to receive(:remote_ref_deletion?).and_return(remote_ref_deletion?)\n      allow(config).to receive(:for_hook).and_return(hook_config)\n    end\n\n    subject { hook.run? }\n\n    context 'with exclude_remotes specified' do\n      let(:hook_config) do\n        { 'exclude_remotes' => exclude_remotes }\n      end\n\n      context 'exclude_remotes is nil' do\n        let(:exclude_remotes) { nil }\n\n        it { subject.should == true }\n      end\n\n      context 'exclude_remotes includes the remote' do\n        let(:exclude_remotes) { [remote_name] }\n\n        it { subject.should == false }\n      end\n\n      context 'exclude_remotes does not include the remote' do\n        let(:exclude_remotes) { ['heroku'] }\n\n        it { subject.should == true }\n      end\n    end\n\n    context 'with include_remote_ref_deletions specified' do\n      let(:hook_config) do\n        { 'include_remote_ref_deletions' => include_remote_ref_deletions }\n      end\n      let(:remote_ref_deletion?) { false }\n      let(:include_remote_ref_deletions) { false }\n\n      context 'when remote branch is not being deleted' do\n        let(:remote_ref_deletion?) { false }\n\n        context 'when include_remote_ref_deletions is not specified' do\n          let(:include_remote_ref_deletions) { nil }\n\n          it { subject.should == true }\n        end\n\n        context 'when include_remote_ref_deletions is false' do\n          let(:include_remote_ref_deletions) { false }\n\n          it { subject.should == true }\n        end\n\n        context 'when include_remote_ref_deletions is true' do\n          let(:include_remote_ref_deletions) { true }\n\n          it { subject.should == true }\n        end\n      end\n\n      context 'when remote branch is being deleted' do\n        let(:remote_ref_deletion?) { true }\n\n        context 'when include_remote_ref_deletions is not specified' do\n          let(:include_remote_ref_deletions) { nil }\n\n          it { subject.should == false }\n        end\n\n        context 'when include_remote_ref_deletions is false' do\n          let(:include_remote_ref_deletions) { false }\n\n          it { subject.should == false }\n        end\n\n        context 'when include_remote_ref_deletions is true' do\n          let(:include_remote_ref_deletions) { true }\n\n          it { subject.should == true }\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/brakeman_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::Brakeman do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when brakeman exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when brakeman exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/cargo_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::CargoTest do\n  let(:config) { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when all tests succeed' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when one test fails' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(stdout: <<-ERRORMSG)\n        running 2 tests\n        test tests::foo ... ok\n        test tests::bar ... FAILED\n\n        failures:\n\n        ---- tests::bar stdout ----\n                thread 'tests::bar' panicked at 'assertion failed: `(left == right)`\n          left: `None`,\n         right: `Some(Bar)`', src/foobar.rs:88:9\n        note: Run with `RUST_BACKTRACE=1` for a backtrace.\n\n\n        failures:\n          tests::bar\n\n        test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n      ERRORMSG\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/flutter_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::FlutterTest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when flutter test exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when flutter test exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          0:03 +0: Counter increments smoke test\n          ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════\n          The following _Exception was thrown running a test:\n          Exception\n\n          When the exception was thrown, this was the stack:\n          #0      main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart:18:5)\n          <asynchronous suspension>\n          #1      main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart)\n          #2      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)\n          <asynchronous suspension>\n          #3      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)\n          #4      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)\n          <asynchronous suspension>\n          #7      TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)\n          #8      AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)\n          #9      FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)\n          #14     withClock (package:clock/src/default.dart:48:10)\n          #15     FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)\n          #20     FakeAsync.run (package:fake_async/fake_async.dart:178:7)\n          #21     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)\n          #22     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)\n          #23     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)\n          <asynchronous suspension>\n          #24     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)\n          #29     Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)\n          #30     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)\n          #35     Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)\n          #36     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)\n          <asynchronous suspension>\n          #37     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)\n          #42     Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)\n          #43     Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)\n          #44     Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)\n          #51     Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)\n          #52     LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)\n          #53     RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)\n          #58     RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)\n          #59     RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)\n          #77     _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)\n          #78     new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)\n          #82     CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)\n          #116    new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)\n          #124    _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)\n          #125    _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)\n          #135    _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)\n          #144    new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)\n          #145    _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)\n          (elided 111 frames from dart:async and package:stack_trace)\n\n          The test description was:\n            Counter increments smoke test\n          ════════════════════════════════════════════════════════════════════════════════════════════════════\n          00:03 +0 -1: Counter increments smoke test [E]\n            Test failed. See exception logs above.\n            The test description was: Counter increments smoke test\n\n          00:03 +0 -1: Some tests failed.\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'with a test failure' do\n      before do\n        result.stub(stderr: '', stdout: <<-MSG)\n          00:02 +0: Counter increments smoke test\n          ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════\n          The following TestFailure object was thrown running a test:\n            Expected: exactly one matching node in the widget tree\n            Actual: _TextFinder:<zero widgets with text \"1\" (ignoring offstage widgets)>\n             Which: means none were found but one was expected\n\n          When the exception was thrown, this was the stack:\n          #4      main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart:19:5)\n          <asynchronous suspension>\n          #5      main.<anonymous closure> (file:///Users/user/project/test/widget_test.dart)\n          #6      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)\n          <asynchronous suspension>\n          #7      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)\n          #8      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)\n          <asynchronous suspension>\n          #11     TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)\n          #12     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)\n          #13     FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)\n          #18     withClock (package:clock/src/default.dart:48:10)\n          #19     FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)\n          #24     FakeAsync.run (package:fake_async/fake_async.dart:178:7)\n          #25     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)\n          #26     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)\n          #27     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)\n          <asynchronous suspension>\n          #28     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)\n          #33     Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)\n          #34     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)\n          #39     Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)\n          #40     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)\n          <asynchronous suspension>\n          #41     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)\n          #46     Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)\n          #47     Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)\n          #48     Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)\n          #55     Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)\n          #56     LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)\n          #57     RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)\n          #62     RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)\n          #63     RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)\n          #81     _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)\n          #82     new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)\n          #86     CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)\n          #120    new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)\n          #128    _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)\n          #129    _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)\n          #139    _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)\n          #148    new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)\n          #149    _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)\n          (elided 111 frames from dart:async and package:stack_trace)\n\n          This was caught by the test expectation on the following line:\n            file:///Users/user/project/test/widget_test.dart line 19\n          The test description was:\n            Counter increments smoke test\n          ════════════════════════════════════════════════════════════════════════════════════════════════════\n          00:02 +0 -1: Counter increments smoke test [E]\n            Test failed. See exception logs above.\n            The test description was: Counter increments smoke test\n\n          00:02 +0 -1: Some tests failed.\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/go_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::GoTest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when go test exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it 'passes' do\n      expect(subject).to pass\n    end\n  end\n\n  context 'when go test exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'when go test returns an error' do\n      let(:error_message) { \"--- FAIL: Test1 (0.00s)\\nFAIL\" }\n\n      before do\n        result.stub(:stdout).and_return(error_message)\n        result.stub(:stderr).and_return('')\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq error_message\n      end\n    end\n\n    context 'when a generic error message is written to stderr' do\n      let(:error_message) { 'go: command not found' }\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return(error_message)\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq error_message\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/golangci_lint_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::GolangciLint do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when golangci-lint exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(success?: true, stderr: '', stdout: '')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it 'passes' do\n      expect(subject).to pass\n    end\n  end\n\n  context 'when golangci-lint exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'when golangci-lint returns an error' do\n      let(:error_message) do\n        'pkg1/file1.go:8:6: exported type `Test` should have comment or be unexported (golint)'\n      end\n\n      before do\n        result.stub(:stdout).and_return(error_message)\n        result.stub(:stderr).and_return('')\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq error_message\n      end\n    end\n\n    context 'when a generic error message is written to stderr' do\n      let(:error_message) { 'golangci-lint: command not found' }\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return(error_message)\n      end\n\n      it 'fails' do\n        expect(subject).to fail_hook\n      end\n\n      it 'returns valid message' do\n        message = subject.run.last\n        expect(message).to eq error_message\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/minitest_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::Minitest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context', all_files: ['test/test_foo.rb']) }\n  subject { described_class.new(config, context) }\n\n  context 'when minitest exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when minitest exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          1) Error:\n          FooTest#test_: foo should bar. :\n          RuntimeError:\n          test/model/foo_test.rb:1:in `block (2 levels) in <class:FooTest>'\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/mix_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::MixTest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when mix test exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when mix test exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/php_unit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::PhpUnit do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when phpunit exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when phpunit exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/pronto_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::Pronto do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  before do\n    subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])\n  end\n\n  context 'when pronto exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pronto exits unsucessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'and it reports an error' do\n      before do\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return([\n          'file2.rb:10 E: IDENTICAL code found in :iter.',\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'and it reports a warning' do\n      before do\n        result.stub(:stderr).and_return('')\n        result.stub(:stdout).and_return <<~MESSAGE\n          Running Pronto::Rubocop\n          file1.rb:12 W: Line is too long. [107/80]\n          file2.rb:14 I: Prefer single-quoted strings\n\n          ```suggestion\n          x = 'x'\n          ```\n        MESSAGE\n      end\n\n      it { should warn }\n    end\n\n    context 'and it has a generic error message written to stderr' do\n      before do\n        result.stub(:stdout).and_return('')\n        result.stub(:stderr).and_return([\n          'Could not find pronto in any of the sources'\n        ].join(\"\\n\"))\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/protected_branches_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/pre_push'\n\ndescribe Overcommit::Hook::PrePush::ProtectedBranches do\n  let(:hook_config) { {} }\n  let(:config) do\n    Overcommit::ConfigurationLoader.default_configuration.merge(\n      Overcommit::Configuration.new(\n        'PrePush' => { 'ProtectedBranches' => hook_config }\n      )\n    )\n  end\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:branch_configurations) do\n    ['master', 'release/*', { 'destructive_only_branch' => nil, 'destructive_only' => true }]\n  end\n  let(:pushed_ref) do\n    instance_double(Overcommit::HookContext::PrePush::PushedRef)\n  end\n\n  before do\n    subject.stub(branch_configurations: branch_configurations)\n    pushed_ref.stub(:remote_ref).and_return(\"refs/heads/#{pushed_ref_name}\")\n    context.stub(:pushed_refs).and_return([pushed_ref])\n  end\n\n  context 'when pushing to unprotected branch' do\n    let(:pushed_ref_name) { 'unprotected-branch' }\n\n    context 'when push is not destructive' do\n      before do\n        pushed_ref.stub(:destructive?).and_return(false)\n      end\n\n      it { should pass }\n    end\n\n    context 'when push is destructive' do\n      before do\n        pushed_ref.stub(:destructive?).and_return(true)\n      end\n\n      it { should pass }\n    end\n  end\n\n  shared_examples_for 'protected branch' do\n    context 'when push is not destructive' do\n      before do\n        pushed_ref.stub(:destructive?).and_return(false)\n      end\n\n      context 'and destructive_only set to false' do\n        let(:hook_config) { { 'destructive_only' => false } }\n\n        it { should fail_hook }\n      end\n\n      context 'and destructive_only set to true' do\n        let(:hook_config) { { 'destructive_only' => true } }\n\n        it { should pass }\n      end\n    end\n\n    context 'when push is destructive' do\n      before do\n        pushed_ref.stub(:destructive?).and_return(true)\n      end\n\n      context 'when destructive_only is set to true' do\n        let(:hook_config) { { 'destructive_only' => true } }\n\n        it { should fail_hook }\n      end\n\n      context 'when destructive_only is set to false' do\n        let(:hook_config) { { 'destructive_only' => false } }\n\n        it { should fail_hook }\n      end\n    end\n  end\n\n  context 'when pushing to protected branch' do\n    context 'when branch name matches a protected branch exactly' do\n      let(:pushed_ref_name) { 'master' }\n      include_examples 'protected branch'\n    end\n\n    context 'when branch name matches a protected branch glob pattern' do\n      let(:pushed_ref_name) { 'release/0.1.0' }\n      include_examples 'protected branch'\n    end\n\n    context 'when branch overwrites global destructive_only' do\n      before do\n        pushed_ref.stub(:destructive?).and_return(true)\n      end\n      let(:pushed_ref_name) { 'destructive_only_branch' }\n      let(:hook_config) { { 'destructive_only' => false } }\n\n      it { should fail_hook }\n    end\n  end\n\n  context 'when pushing tags' do\n    let(:pushed_ref_name) { 'redundant' }\n\n    before do\n      pushed_ref.stub(:remote_ref).and_return(\"refs/tags/#{pushed_ref_name}\")\n    end\n\n    it { should pass }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/pub_test_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::PubTest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when pub test exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pub test exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E]\n            Exception\n            test/test_test.dart 6:5  main.<fn>\n\n          00:01 +1 -1: Some tests failed.\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'with a test failure' do\n      before do\n        result.stub(stderr: '', stdout: <<-MSG)\n          00:01 +0 -1: test/test_test.dart: String.split() splits the string on the delimiter [E]\n            Expected: ['fooo', 'bar', 'baz']\n              Actual: ['foo', 'bar', 'baz']\n               Which: at location [0] is 'foo' instead of 'fooo'\n\n            package:test_api         expect\n            test/test_test.dart 6:5  main.<fn>\n\n          00:01 +1 -1: Some tests failed.\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/pytest_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::Pytest do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when pytest exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when pytest exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/python_nose_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::PythonNose do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject       { described_class.new(config, context) }\n\n  context 'when nose exits successfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when nose exits unsucessfully' do\n    before do\n      result = double('result')\n      result.stub(:success?).and_return(false)\n      result.stub(:stdout).and_return('Some error message')\n      result.stub(:stderr).and_return('')\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should fail_hook 'Some error message' }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/r_spec_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::RSpec do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'when rspec exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n\n    it {\n      expect(subject).to receive(:execute).with(['rspec']).and_return(result)\n\n      subject.run\n    }\n  end\n\n  context 'with included files set' do\n    let(:result) { double('result') }\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'PrePush' => {\n                        'RSpec' => {\n                          'include' => ['**/*_spec.rb'],\n                        }\n                      }\n      ))\n    end\n\n    let(:context) { double('context') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n      subject.stub(:applicable_files).and_return('spec/test_spec.rb')\n    end\n\n    it { should pass }\n\n    it {\n      expect(subject).to receive(:execute).with(['rspec'],\n                                                args: 'spec/test_spec.rb').and_return(result)\n\n      subject.run\n    }\n  end\n\n  context 'when rspec exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `load': /home/user/dev/github/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:49: can't find string \"EOS\" anywhere before EOF (SyntaxError)\n          /home/user/dev/overcommit/spec/overcommit/hook/pre_push/rspec_spec.rb:29: syntax error, unexpected end-of-input\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `block in load_spec_files'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `each'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1224:in `load_spec_files'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:97:in `setup'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:85:in `run'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:70:in `run'\n            from /home/user/.rbenv/gems/2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/runner.rb:38:in `invoke'\n            from /home/user/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.2/exe/rspec:4:in `<top (required)>'\n            from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `load'\n            from /home/user/.rbenv/versions/2.2.1/bin/rspec:23:in `<main>'\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n\n    context 'with a test failure' do\n      before do\n        result.stub(stderr: '', stdout: <<-MSG)\n          .FF\n\n          Failures:\n\n            1) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail\n               Failure/Error: it { should fail_hook }\n                 expected that the hook would fail\n               # ./spec/overcommit/hook/pre_push/rspec_spec.rb:45:in `block (4 levels) in <top (required)>'\n\n            2) Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail\n               Failure/Error: it { should fail_hook }\n                 expected that the hook would fail\n               # ./spec/overcommit/hook/pre_push/rspec_spec.rb:57:in `block (4 levels) in <top (required)>'\n\n          Finished in 0.00505 seconds (files took 0.27437 seconds to load)\n          3 examples, 2 failures\n\n          Failed examples:\n\n          rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:45 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a runtime error should fail\n          rspec ./spec/overcommit/hook/pre_push/rspec_spec.rb:57 # Overcommit::Hook::PrePush::RSpec when rspec exits unsuccessfully with a test failure should fail\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/rake_target_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::RakeTarget do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  context 'without targets parameters' do\n    let(:result) { double('result') }\n    it 'raises' do\n      expect { subject.run }.to raise_error(\n        RuntimeError, /RakeTarget: targets parameter is empty.*/\n      )\n    end\n  end\n\n  context 'with targets parameter set' do\n    let(:config) do\n      super().merge(Overcommit::Configuration.new(\n                      'PrePush' => {\n                        'RakeTarget' => {\n                          'targets' => ['test'],\n                        }\n                      }\n      ))\n    end\n    let(:result) { double('result') }\n\n    context 'when rake exits successfully' do\n      before do\n        result.stub(:success?).and_return(true)\n        subject.stub(:execute).and_return(result)\n        result.stub(:stdout).and_return('ANYTHING')\n      end\n\n      it { should pass }\n    end\n\n    context 'when rake exits unsuccessfully' do\n      before do\n        result.stub(:success?).and_return(false)\n        subject.stub(:execute).and_return(result)\n        result.stub(:stdout).and_return('ANYTHING')\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_push/test_unit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PrePush::TestUnit do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context', all_files: ['test/foo_test.rb']) }\n\n  subject { described_class.new(config, context) }\n\n  context 'when test-unit exits successfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(true)\n      subject.stub(:execute).and_return(result)\n    end\n\n    it { should pass }\n  end\n\n  context 'when test-unit exits unsuccessfully' do\n    let(:result) { double('result') }\n\n    before do\n      result.stub(:success?).and_return(false)\n      subject.stub(:execute).and_return(result)\n    end\n\n    context 'with a runtime error' do\n      before do\n        result.stub(stdout: '', stderr: <<-MSG)\n          1) Error:\n          FooTest#test_: foo should bar. :\n          RuntimeError:\n          test/model/foo_test.rb:1:in `block (2 levels) in <class:FooTest>'\n        MSG\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/pre_rebase/merged_commits_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Hook::PreRebase::MergedCommits do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { double('context') }\n  subject { described_class.new(config, context) }\n\n  let(:master_branch) { 'master' }\n\n  before do\n    subject.stub(:branches) { [master_branch] }\n  end\n\n  context 'when rebasing a detached HEAD' do\n    before do\n      context.stub(:detached_head?) { true }\n    end\n\n    it { should pass }\n  end\n\n  context 'when there are no commits to rebase' do\n    before do\n      context.stub(:detached_head?) { false }\n      context.stub(:rebased_commits) { [] }\n    end\n\n    it { should pass }\n  end\n\n  context 'when there are commits to rebase' do\n    let(:commit_sha1) { random_hash }\n    let(:rebased_branch) { 'topic' }\n\n    before do\n      context.stub(:detached_head?) { false }\n      context.stub(:rebased_commits) { [commit_sha1] }\n    end\n\n    context 'when commits have not yet been merged' do\n      before do\n        Overcommit::GitRepo.stub(:branches_containing_commit).\n          with(commit_sha1) { [rebased_branch] }\n      end\n\n      it { should pass }\n    end\n\n    context 'when commits have already been merged' do\n      before do\n        Overcommit::GitRepo.stub(:branches_containing_commit).\n          with(commit_sha1) { [rebased_branch, master_branch] }\n      end\n\n      it { should fail_hook }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/prepare_commit_msg/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/prepare_commit_msg'\n\ndescribe Overcommit::Hook::PrepareCommitMsg::Base do\n  let(:config)  { Overcommit::ConfigurationLoader.default_configuration }\n  let(:context) { Overcommit::HookContext::PrepareCommitMsg.new(config, [], StringIO.new) }\n  let(:printer) { double('printer') }\n\n  context 'when multiple hooks run simultaneously' do\n    let(:hook_1) { described_class.new(config, context) }\n    let(:hook_2) { described_class.new(config, context) }\n\n    let(:tempfile) { 'test-prepare-commit-msg.txt' }\n\n    let(:initial_content) { \"This is a test\\n\" }\n\n    before do\n      File.open(tempfile, 'w') do |f|\n        f << initial_content\n      end\n    end\n\n    after do\n      File.delete(tempfile)\n    end\n\n    it 'works well with concurrency' do\n      allow(context).to receive(:commit_message_filename).and_return(tempfile)\n      allow(hook_1).to receive(:run) do\n        hook_1.modify_commit_message do |contents|\n          \"alpha\\n\" + contents\n        end\n      end\n      allow(hook_2).to receive(:run) do\n        hook_2.modify_commit_message do |contents|\n          contents + \"bravo\\n\"\n        end\n      end\n      t1 = Thread.new { hook_1.run }\n      t2 = Thread.new { hook_2.run }\n      [t1, t2].each(&:join)\n      expect(File.read(tempfile)).to match(/alpha\\n#{initial_content}bravo\\n/m)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook/prepare_commit_msg/replace_branch_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/prepare_commit_msg'\n\ndescribe Overcommit::Hook::PrepareCommitMsg::ReplaceBranch do\n  def checkout_branch(branch)\n    allow(Overcommit::GitRepo).to receive(:current_branch).and_return(branch)\n  end\n\n  def new_config(opts = {})\n    default = Overcommit::ConfigurationLoader.default_configuration\n\n    return default if opts.empty?\n\n    default.merge(\n      Overcommit::Configuration.new(\n        'PrepareCommitMsg' => {\n          'ReplaceBranch' => opts.merge('enabled' => true)\n        }\n      )\n    )\n  end\n\n  def new_context(config, argv)\n    Overcommit::HookContext::PrepareCommitMsg.new(config, argv, StringIO.new)\n  end\n\n  def hook_for(config, context)\n    described_class.new(config, context)\n  end\n\n  def add_file(name, contents)\n    File.open(name, 'w') { |f| f.puts contents }\n  end\n\n  def remove_file(name)\n    File.delete(name)\n  end\n\n  before { allow(Overcommit::Utils).to receive_message_chain(:log, :debug) }\n\n  let(:config)           { new_config }\n  let(:normal_context)   { new_context(config, ['COMMIT_EDITMSG']) }\n  let(:message_context)  { new_context(config, %w[COMMIT_EDITMSG message]) }\n  let(:commit_context)   { new_context(config, %w[COMMIT_EDITMSG commit HEAD]) }\n  let(:merge_context)    { new_context(config, %w[MERGE_MSG merge]) }\n  let(:squash_context)   { new_context(config, %w[SQUASH_MSG squash]) }\n  let(:template_context) { new_context(config, ['template.txt', 'template']) }\n  subject(:hook)         { hook_for(config, normal_context) }\n\n  describe '#run' do\n    before { add_file    'COMMIT_EDITMSG', '' }\n    after  { remove_file 'COMMIT_EDITMSG' }\n\n    context 'when the checked out branch matches the pattern' do\n      before { checkout_branch '123-topic' }\n      before { hook.run }\n\n      it { is_expected.to pass }\n\n      it 'prepends the replacement text' do\n        expect(File.read('COMMIT_EDITMSG')).to eq(\"[#123]\\n\")\n      end\n\n      context 'when the replacement text contains a space' do\n        let(:config) { new_config('replacement_text' => '[\\1] ') }\n\n        it 'prepends the replacement text, including the space' do\n          expect(File.read('COMMIT_EDITMSG')).to eq(\"[123] \\n\")\n        end\n      end\n\n      context 'when skip_if exits with a zero status' do\n        let(:config) { new_config('skip_if' => ['bash', '-c', 'exit 0']) }\n\n        it { is_expected.to pass }\n\n        it 'does not change the commit message' do\n          expect(File.read('COMMIT_EDITMSG')).to eq(\"\\n\")\n        end\n      end\n\n      context 'when skip_if exits with a non-zero status' do\n        let(:config) { new_config('skip_if' => ['bash', '-c', 'exit 1']) }\n\n        it { is_expected.to pass }\n\n        it 'does change the commit message' do\n          expect(File.read('COMMIT_EDITMSG')).to eq(\"[#123]\\n\")\n        end\n      end\n    end\n\n    context \"when the checked out branch doesn't matches the pattern\" do\n      before { checkout_branch 'topic-123' }\n      before { hook.run }\n\n      context 'with the default `skipped_commit_types`' do\n        it { is_expected.to warn }\n      end\n\n      context 'when merging, and `skipped_commit_types` includes `merge`' do\n        let(:config)   { new_config('skipped_commit_types' => ['merge']) }\n        subject(:hook) { hook_for(config, merge_context) }\n\n        it { is_expected.to pass }\n      end\n\n      context 'when merging, and `skipped_commit_types` includes `template`' do\n        let(:config)   { new_config('skipped_commit_types' => ['template']) }\n        subject(:hook) { hook_for(config, template_context) }\n\n        it { is_expected.to pass }\n      end\n\n      context 'when merging, and `skipped_commit_types` includes `message`' do\n        let(:config)   { new_config('skipped_commit_types' => ['message']) }\n        subject(:hook) { hook_for(config, message_context) }\n\n        it { is_expected.to pass }\n      end\n\n      context 'when merging, and `skipped_commit_types` includes `commit`' do\n        let(:config)   { new_config('skipped_commit_types' => ['commit']) }\n        subject(:hook) { hook_for(config, commit_context) }\n\n        it { is_expected.to pass }\n      end\n\n      context 'when merging, and `skipped_commit_types` includes `squash`' do\n        let(:config)   { new_config('skipped_commit_types' => ['squash']) }\n        subject(:hook) { hook_for(config, squash_context) }\n\n        it { is_expected.to pass }\n      end\n    end\n\n    context 'when the replacement text points to a valid filename' do\n      before { checkout_branch '123-topic' }\n      before { add_file    'replacement_text.txt', 'FOO' }\n      after  { remove_file 'replacement_text.txt' }\n\n      let(:config) { new_config('replacement_text' => 'replacement_text.txt') }\n      let(:normal_context) { new_context(config, ['COMMIT_EDITMSG']) }\n      subject(:hook)       { hook_for(config, normal_context) }\n\n      before { hook.run }\n\n      it { is_expected.to pass }\n\n      let(:commit_msg) { File.read('COMMIT_EDITMSG') }\n\n      it 'uses the file contents as the replacement text' do\n        expect(commit_msg).to eq(File.read('replacement_text.txt'))\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/base_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::HookContext::Base do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#hook_class_name' do\n    subject { context.hook_class_name }\n\n    it 'returns the short class name of the context' do\n      subject.should == 'Base'\n    end\n  end\n\n  describe '#input_lines' do\n    subject { context.input_lines }\n\n    before do\n      input.stub(:read).and_return(\"line 1\\nline 2\\n\")\n    end\n\n    it { should == ['line 1', 'line 2'] }\n  end\n\n  describe '#post_fail_message' do\n    subject { context.post_fail_message }\n\n    it { should be_nil }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/commit_msg_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/commit_msg'\n\ndescribe Overcommit::HookContext::CommitMsg do\n  let(:comment_char) { '#' }\n  let(:config) { double('config') }\n  let(:args) { [commit_message_file] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n  before do\n    Overcommit::GitConfig.stub(:comment_character).and_return(comment_char)\n  end\n  let(:commit_msg) do\n    [\n      '# Please enter the commit message for your changes.',\n      'Some commit message',\n      '# On branch master',\n      'diff --git a/file b/file',\n      'index 4ae1030..342a117 100644',\n      '--- a/file',\n      '+++ b/file',\n    ]\n  end\n\n  let(:commit_message_file) do\n    Tempfile.new('commit-message').tap do |file|\n      file.write(commit_msg.join(\"\\n\"))\n      file.fsync\n    end.path\n  end\n\n  describe '#commit_message' do\n    subject { context.commit_message }\n\n    it 'strips comments and trailing diff' do\n      subject.should == \"Some commit message\\n\"\n    end\n\n    context 'with alternate comment character' do\n      let(:comment_char) { '!' }\n      let(:commit_msg) do\n        [\n          '! Please enter the commit message for your changes.',\n          'Some commit message',\n          '! On branch master',\n          'diff --git a/file b/file',\n          'index 4ae1030..342a117 100644',\n          '--- a/file',\n          '+++ b/file',\n        ]\n      end\n\n      it 'strips comments and trailing diff' do\n        subject.should == \"Some commit message\\n\"\n      end\n    end\n  end\n\n  describe '#commit_message_lines' do\n    subject { context.commit_message_lines }\n\n    it 'strips comments and trailing diff' do\n      subject.should == [\"Some commit message\\n\"]\n    end\n  end\n\n  describe '#empty_message?' do\n    subject { context.empty_message? }\n\n    context 'when commit message is empty' do\n      let(:commit_msg) { [] }\n\n      it { should == true }\n    end\n\n    context 'when commit message contains only whitespace' do\n      let(:commit_msg) { [' '] }\n\n      it { should == true }\n    end\n\n    context 'when commit message is not empty' do\n      let(:commit_msg) { ['Some commit message'] }\n\n      it { should == false }\n    end\n  end\n\n  describe '#post_fail_message' do\n    subject { context.post_fail_message }\n\n    it 'returns printable log of commit message' do\n      subject.should start_with \"Failed commit message:\\nSome commit message\\n\"\n    end\n\n    it 'returns the command to run to restore the commit message' do\n      subject.should end_with \"git commit --edit --file=#{commit_message_file}\"\n    end\n  end\n\n  describe '#amendment?' do\n    subject { context.amendment? }\n\n    before do\n      Overcommit::Utils.stub(:parent_command).and_return(command)\n    end\n\n    context 'when amending a commit using `git commit --amend`' do\n      let(:command) { 'git commit --amend' }\n\n      it { should == true }\n    end\n\n    context 'when the parent command contains invalid byte sequence' do\n      let(:command) { \"git commit --amend -m \\xE3M^AM^B\" }\n\n      it { should == true }\n    end\n\n    context 'when amending a commit using a git alias' do\n      around do |example|\n        repo do\n          `git config alias.amend \"commit --amend\"`\n          `git config alias.other-amend \"commit --amend\"`\n          example.run\n        end\n      end\n\n      context 'when using one of multiple aliases' do\n        let(:command) { 'git amend' }\n\n        it { should == true }\n      end\n\n      context 'when using another of multiple aliases' do\n        let(:command) { 'git other-amend' }\n\n        it { should == true }\n      end\n    end\n\n    context 'when not amending a commit' do\n      context 'using `git commit`' do\n        let(:command) { 'git commit' }\n\n        it { should == false }\n      end\n\n      context 'using a git alias containing \"--amend\"' do\n        let(:command) { 'git no--amend' }\n\n        around do |example|\n          repo do\n            `git config alias.no--amend commit`\n            example.run\n          end\n        end\n\n        it { should == false }\n      end\n    end\n  end\n\n  describe '#setup_environment' do\n    subject { context.setup_environment }\n\n    context 'when there are no staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'other-tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'keeps already-committed files' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\n\"\n      end\n\n      it 'does not keep unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.should == \"Hello Other World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when there are staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'tracked-file', append: true)\n          echo('Some more text', 'other-tracked-file', append: true)\n          `git add tracked-file`\n          echo('Yet some more text', 'tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'keeps staged changes' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\nSome more text\\n\"\n      end\n\n      it 'does not keep unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.should == \"Hello Other World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when all changes have been staged' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          echo('Hello Other World', 'other-tracked-file')\n          `git add other-tracked-file`\n          example.run\n        end\n      end\n\n      it 'does not stash changes' do\n        expect(context.private_methods).to include :stash_changes\n        expect(context).not_to receive(:stash_changes)\n        subject\n      end\n    end\n\n    context 'when renaming a file during an amendment' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          touch 'some-file'\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          `git mv some-file renamed-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it 'does not try to update modification time of the old non-existent file' do\n        File.should_receive(:mtime).with(/renamed-file/)\n        File.should_not_receive(:mtime).with(/some-file/)\n        subject\n      end\n    end\n\n    context 'when only a submodule change is staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          `git add sub`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n    end\n\n    # Git cannot track Windows symlinks\n    unless Overcommit::OS.windows?\n      context 'when a broken symlink is staged' do\n        around do |example|\n          repo do\n            Overcommit::Utils::FileUtils.symlink('non-existent-file', 'symlink')\n            `git add symlink`\n            example.run\n          end\n        end\n\n        it 'does not attempt to update/restore the modification time of the file' do\n          File.should_not_receive(:mtime)\n          File.should_not_receive(:utime)\n          subject\n        end\n      end\n    end\n  end\n\n  describe '#cleanup_environment' do\n    subject { context.cleanup_environment }\n\n    before do\n      context.setup_environment\n    end\n\n    context 'when there were no staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'other-tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'restores the unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.\n          should == \"Hello Other World\\nSome more text\\n\"\n      end\n\n      it 'keeps already-committed files' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when there were staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'tracked-file', append: true)\n          echo('Some more text', 'other-tracked-file', append: true)\n          `git add tracked-file`\n          echo('Yet some more text', 'tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'restores the unstaged changes' do\n        subject\n        File.open('tracked-file', 'r').read.\n          should == \"Hello World\\nSome more text\\nYet some more text\\n\"\n      end\n\n      it 'keeps staged changes' do\n        subject\n        `git show :tracked-file`.should == \"Hello World\\nSome more text\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when all changes were staged' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          echo('Hello Other World', 'other-tracked-file')\n          `git add other-tracked-file`\n          example.run\n        end\n      end\n\n      it 'does not touch the working tree' do\n        expect(context.private_methods).to include :clear_working_tree\n        expect(context.private_methods).to include :restore_working_tree\n        expect(context).not_to receive(:clear_working_tree)\n        expect(context).not_to receive(:restore_working_tree)\n        subject\n      end\n    end\n\n    context 'when there were deleted files' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          `git rm tracked-file`\n          example.run\n        end\n      end\n\n      it 'deletes the file' do\n        subject\n        File.exist?('tracked-file').should == false\n      end\n    end\n\n    context 'when only a submodule change was staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          `git add sub`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n    end\n\n    context 'when submodule changes were staged along with other changes' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          echo('Hello Again', 'tracked-file')\n          `git add sub tracked-file`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n\n      it 'keeps staged file change' do\n        subject\n        `git show :tracked-file`.should == \"Hello Again\\n\"\n      end\n    end\n\n    context 'when a submodule removal was staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          `git rm sub`\n          example.run\n        end\n      end\n\n      it 'does not leave behind an empty submodule directory' do\n        subject\n        File.exist?('sub').should == false\n      end\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    before do\n      context.stub(:amendment?).and_return(false)\n    end\n\n    it 'does not include submodules' do\n      submodule = repo do\n        touch 'foo'\n        `git add foo`\n        `git commit -m \"Initial commit\"`\n      end\n\n      repo do\n        `git -c protocol.file.allow=always submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n        expect(subject).to_not include File.expand_path('test-sub')\n      end\n    end\n\n    context 'when no files were staged' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were added' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were modified' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          echo('Hello', 'some-file')\n          `git add some-file`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were deleted' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git rm some-file`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when amending last commit' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          touch('other-file')\n          `git add other-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it { should =~ [File.expand_path('some-file'), File.expand_path('other-file')] }\n    end\n\n    context 'when renaming a file during an amendment' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          touch 'some-file'\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          `git mv some-file renamed-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it 'does not include the old file name in the list of modified files' do\n        subject.should_not include File.expand_path('some-file')\n      end\n    end\n\n    # Git cannot track Windows symlinks\n    unless Overcommit::OS.windows?\n      context 'when changing a symlink to a directory during an amendment' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            FileUtils.mkdir 'some-directory'\n            symlink('some-directory', 'some-symlink')\n            `git add some-symlink some-directory`\n            `git commit -m \"Add file\"`\n            `git rm some-symlink`\n            FileUtils.mkdir 'some-symlink'\n            touch File.join('some-symlink', 'another-file')\n            `git add some-symlink`\n            example.run\n          end\n        end\n\n        before do\n          context.stub(:amendment?).and_return(true)\n        end\n\n        it 'does not include the directory in the list of modified files' do\n          subject.should_not include File.expand_path('some-symlink')\n        end\n      end\n\n      context 'when breaking a symlink during an amendment' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            FileUtils.mkdir 'some-directory'\n            touch File.join('some-directory', 'some-file')\n            symlink('some-directory', 'some-symlink')\n            `git add some-symlink some-directory`\n            `git commit -m \"Add file\"`\n            `git rm -rf some-directory`\n            example.run\n          end\n        end\n\n        before do\n          context.stub(:amendment?).and_return(true)\n        end\n\n        it 'still includes the broken symlink in the list of modified files' do\n          subject.should include File.expand_path('some-symlink')\n        end\n      end\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    before do\n      context.stub(:amendment?).and_return(false)\n    end\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when amending last commit' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          File.open(modified_file, 'a') { |f| f.puts 4 }\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it { should == Set.new(1..4) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/diff_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/diff'\n\ndescribe Overcommit::HookContext::Diff do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input, diff: 'master') }\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    context 'when repo contains no files' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b other-branch 2>&1`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when the repo contains files that are unchanged from the ref' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          touch('some-other-file')\n          `git add some-other-file`\n          `git commit -m \"Add files\"`\n          `git checkout -b other-branch 2>&1`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when repo contains files that have been changed from the ref' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          touch('some-other-file')\n          `git add some-other-file`\n          `git commit -m \"Add files\"`\n          `git checkout -b other-branch 2>&1`\n          File.open('some-file', 'w') { |f| f.write(\"hello\\n\") }\n          `git add some-file`\n          `git commit -m \"Edit file\"`\n          example.run\n        end\n      end\n\n      it { should == %w[some-file].map { |file| File.expand_path(file) } }\n    end\n\n    context 'when repo contains submodules' do\n      around do |example|\n        submodule = repo do\n          touch 'foo'\n          `git add foo`\n          `git commit -m \"Initial commit\"`\n        end\n\n        repo do\n          `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b other-branch 2>&1`\n          example.run\n        end\n      end\n\n      it { should_not include File.expand_path('test-sub') }\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          touch(modified_file)\n          `git add #{modified_file}`\n          `git commit -m \"Add file\"`\n          `git checkout -b other-branch 2>&1`\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Edit file\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          touch(modified_file)\n          `git add #{modified_file}`\n          `git commit -m \"Add file\"`\n          `git checkout -b other-branch 2>&1`\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n          `git add #{modified_file}`\n          `git commit -m \"Edit file\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n  end\n\n  describe '#hook_type_name' do\n    subject { context.hook_type_name }\n\n    it { should == 'pre_commit' }\n  end\n\n  describe '#hook_script_name' do\n    subject { context.hook_script_name }\n\n    it { should == 'pre-commit' }\n  end\n\n  describe '#initial_commit?' do\n    subject { context.initial_commit? }\n\n    before { Overcommit::GitRepo.stub(:initial_commit?).and_return(true) }\n\n    it { should == true }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/post_checkout_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/post_checkout'\n\ndescribe Overcommit::HookContext::PostCheckout do\n  let(:config) { double('config') }\n  let(:args) { [previous_head, new_head, branch_flag] }\n  let(:input) { double('input') }\n  let(:previous_head) { random_hash }\n  let(:new_head) { random_hash }\n  let(:branch_flag) { '1' }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#previous_head' do\n    subject { context.previous_head }\n\n    it { should == previous_head }\n  end\n\n  describe '#new_head' do\n    subject { context.new_head }\n\n    it { should == new_head }\n  end\n\n  describe '#branch_checkout?' do\n    subject { context.branch_checkout? }\n\n    context 'when the flag is 0' do\n      let(:branch_flag) { '0' }\n\n      it { should == false }\n    end\n\n    context 'when the flag is 1' do\n      it { should == true }\n    end\n  end\n\n  describe '#file_checkout?' do\n    subject { context.file_checkout? }\n\n    context 'when the flag is 0' do\n      let(:branch_flag) { '0' }\n\n      it { should == true }\n    end\n\n    context 'when the flag is 1' do\n      it { should == false }\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    let(:new_head) { 'HEAD' }\n    let(:previous_head) { 'HEAD~' }\n\n    it 'does not include submodules' do\n      submodule = repo do\n        touch 'foo'\n        `git add foo`\n        `git commit -m \"Initial commit\"`\n      end\n\n      repo do\n        `git commit --allow-empty -m \"Initial commit\"`\n        `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n        `git commit -m \"Add submodule\"`\n        expect(subject).to_not include File.expand_path('test-sub')\n      end\n    end\n\n    context 'when no files were modified' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git commit --allow-empty -m \"Another commit\"`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were added' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were modified' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          echo('Hello', 'some-file')\n          `git add some-file`\n          `git commit -m \"Modify file\"`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were deleted' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git rm some-file`\n          `git commit -m \"Delete file\"`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were renamed' do\n      around do |example|\n        repo do\n          touch 'some-file'\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          `git mv some-file renamed-file`\n          `git commit -m \"Rename file\"`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('renamed-file')] }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/post_commit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/post_commit'\n\ndescribe Overcommit::HookContext::PostCommit do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    it 'does not include submodules' do\n      submodule = repo do\n        touch 'foo'\n        `git add foo`\n        `git commit -m \"Initial commit\"`\n      end\n\n      repo do\n        `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n        `git commit -m \"Initial commit\"`\n        expect(subject).to_not include File.expand_path('test-sub')\n      end\n    end\n\n    context 'when no files were staged' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were added' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were modified' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          echo('Hello', 'some-file')\n          `git add some-file`\n          `git commit -m \"Modify some-file\"`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were deleted' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git rm some-file`\n          `git commit -m \"Delete some-file\"`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n  end\n\n  describe '#initial_commit?' do\n    subject { context.initial_commit? }\n\n    context 'when a previous commit exists' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git commit --allow-empty -m \"Another commit\"`\n          example.run\n        end\n      end\n\n      it { should == false }\n    end\n\n    context 'when no previous commit exists' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          example.run\n        end\n      end\n\n      it { should == true }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/post_merge_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/post_merge'\n\ndescribe Overcommit::HookContext::PostMerge do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#squash?' do\n    subject { context.squash? }\n\n    context 'when the merge is made using --squash' do\n      let(:args) { [1] }\n\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git commit --allow-empty -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --squash child`\n          example.run\n        end\n      end\n\n      it { should == true }\n    end\n\n    context 'when the merge is made without --squash' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git commit --allow-empty -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == false }\n    end\n  end\n\n  describe '#merge_commit?' do\n    subject { context.merge_commit? }\n\n    context 'when the merge is made using --squash' do\n      let(:args) { [1] }\n\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git commit --allow-empty -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --squash child`\n          example.run\n        end\n      end\n\n      it { should == false }\n    end\n\n    context 'when the merge is made without --squash' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git commit --allow-empty -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == true }\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    it 'does not include submodules' do\n      submodule = repo do\n        touch 'foo'\n        `git add foo`\n        `git commit -m \"Initial commit\"`\n      end\n\n      repo do\n        `git commit --allow-empty -m \"Initial commit\"`\n        `git checkout -b child > #{File::NULL} 2>&1`\n        `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n        `git commit -m \"Add submodule\"`\n        `git checkout master > #{File::NULL} 2>&1`\n        `git merge --no-ff --no-edit child`\n        expect(subject).to_not include File.expand_path('test-sub')\n      end\n    end\n\n    context 'when no files were staged' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git commit --allow-empty -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were added' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were modified' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          echo('Hello', 'some-file')\n          `git add some-file`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were deleted' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          `git rm some-file`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when the merge is made using --squash' do\n      let(:args) { [1] }\n\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --squash child`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n          `git add #{modified_file}`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --no-ff --no-edit child`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when the merge is made using --squash' do\n      let(:args) { [1] }\n\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git checkout -b child > #{File::NULL} 2>&1`\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Branch commit\"`\n          `git checkout master > #{File::NULL} 2>&1`\n          `git merge --squash child`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/post_rewrite_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/post_rewrite'\n\ndescribe Overcommit::HookContext::PostRewrite do\n  let(:config) { double('config') }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#amend?' do\n    subject { context.amend? }\n\n    context 'when rewrite was triggered by amend' do\n      let(:args) { ['amend'] }\n\n      it { should == true }\n    end\n\n    context 'when rewrite was triggered by rebase' do\n      let(:args) { ['rebase'] }\n\n      it { should == false }\n    end\n  end\n\n  describe '#rebase?' do\n    subject { context.rebase? }\n\n    context 'when rewrite was triggered by amend' do\n      let(:args) { ['amend'] }\n\n      it { should == false }\n    end\n\n    context 'when rewrite was triggered by rebase' do\n      let(:args) { ['rebase'] }\n\n      it { should == true }\n    end\n  end\n\n  describe '#rewritten_commits' do\n    subject(:rewritten_commits) { context.rewritten_commits }\n\n    let(:old_hash_1) { random_hash }\n    let(:new_hash_1) { random_hash }\n    let(:old_hash_2) { random_hash }\n    let(:new_hash_2) { random_hash }\n\n    context 'when rewrite was triggered by amend' do\n      let(:args) { ['amend'] }\n\n      before do\n        input.stub(:read).and_return(\"#{old_hash_1} #{new_hash_1}\\n\")\n      end\n\n      it 'should parse rewritten commit info from the input' do\n        rewritten_commits.length.should == 1\n        rewritten_commits[0].old_hash.should == old_hash_1\n        rewritten_commits[0].new_hash.should == new_hash_1\n      end\n    end\n\n    context 'when rewrite was triggered by rebase' do\n      let(:args) { ['rebase'] }\n\n      before do\n        input.stub(:read).and_return([\n          \"#{old_hash_1} #{new_hash_1}\",\n          \"#{old_hash_2} #{new_hash_2}\"\n        ].join(\"\\n\"))\n      end\n\n      it 'should parse rewritten commit info from the input' do\n        rewritten_commits.length.should == 2\n        rewritten_commits[0].old_hash.should == old_hash_1\n        rewritten_commits[0].new_hash.should == new_hash_1\n        rewritten_commits[1].old_hash.should == old_hash_2\n        rewritten_commits[1].new_hash.should == new_hash_2\n      end\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    before do\n      context.stub(:rewritten_commits).and_return(rewritten_commits)\n    end\n\n    context 'when rewrite was triggered by amend' do\n      let(:args) { ['amend'] }\n      let(:rewritten_commits) { [double(old_hash: 'HEAD@{1}', new_hash: 'HEAD')] }\n\n      it 'does not include submodules' do\n        submodule = repo do\n          touch 'foo'\n          `git add foo`\n          `git commit -m \"Initial commit\"`\n        end\n\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n          `git commit --amend -m \"Add submodule\"`\n          expect(subject).to_not include File.expand_path('test-sub')\n        end\n      end\n\n      context 'when no files were modified' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            `git commit --amend --allow-empty -m \"Another commit\"`\n            example.run\n          end\n        end\n\n        it { should be_empty }\n      end\n\n      context 'when files were added' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            touch('some-file')\n            `git add some-file`\n            `git commit --amend -m \"Add file\"`\n            example.run\n          end\n        end\n\n        it { should == [File.expand_path('some-file')] }\n      end\n\n      context 'when files were modified' do\n        around do |example|\n          repo do\n            touch('some-file')\n            `git add some-file`\n            `git commit -m \"Initial commit\"`\n            echo('Hello', 'some-file')\n            `git add some-file`\n            `git commit --amend -m \"Modify file\"`\n            example.run\n          end\n        end\n\n        it { should == [File.expand_path('some-file')] }\n      end\n\n      context 'when files were deleted' do\n        around do |example|\n          repo do\n            touch('some-file')\n            `git add some-file`\n            `git commit -m \"Initial commit\"`\n            `git rm some-file`\n            `git commit --amend --allow-empty -m \"Delete file\"`\n            example.run\n          end\n        end\n\n        it { should be_empty }\n      end\n\n      context 'when files were renamed' do\n        around do |example|\n          repo do\n            touch 'some-file'\n            `git add some-file`\n            `git commit -m \"Add file\"`\n            `git mv some-file renamed-file`\n            `git commit --amend -m \"Rename file\"`\n            example.run\n          end\n        end\n\n        it { should == [File.expand_path('renamed-file')] }\n      end\n\n      # Git cannot track Windows symlinks\n      unless Overcommit::OS.windows?\n        context 'when changing a symlink to a directory during an amendment' do\n          around do |example|\n            repo do\n              FileUtils.mkdir 'some-directory'\n              symlink('some-directory', 'some-symlink')\n              `git add some-symlink some-directory`\n              `git commit -m \"Add file\"`\n              `git rm some-symlink`\n              FileUtils.mkdir 'some-symlink'\n              touch File.join('some-symlink', 'another-file')\n              `git add some-symlink`\n              `git commit --amend -m \"Change symlink to directory\"`\n              example.run\n            end\n          end\n\n          it 'does not include the directory in the list of modified files' do\n            subject.should_not include File.expand_path('some-symlink')\n          end\n        end\n\n        context 'when breaking a symlink during an amendment' do\n          around do |example|\n            repo do\n              FileUtils.mkdir 'some-directory'\n              touch File.join('some-directory', 'some-file')\n              symlink('some-directory', 'some-symlink')\n              `git add some-symlink some-directory`\n              `git commit -m \"Add file\"`\n              `git rm -rf some-directory`\n              `git commit --amend -m \"Remove directory to break symlink\"`\n              example.run\n            end\n          end\n\n          it 'does not include the broken symlink in the list of modified files' do\n            subject.should_not include File.expand_path('some-symlink')\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/pre_commit_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/pre_commit'\n\ndescribe Overcommit::HookContext::PreCommit do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#amendment?' do\n    subject { context.amendment? }\n\n    before do\n      Overcommit::Utils.stub(:parent_command).and_return(command)\n    end\n\n    context 'when amending a commit using `git commit --amend`' do\n      let(:command) { 'git commit --amend' }\n\n      it { should == true }\n    end\n\n    context 'when the parent command contains invalid byte sequence' do\n      let(:command) { \"git commit --amend -m \\xE3M^AM^B\" }\n\n      it { should == true }\n    end\n\n    context 'when amending a commit using a git alias' do\n      around do |example|\n        repo do\n          `git config alias.amend \"commit --amend\"`\n          `git config alias.other-amend \"commit --amend\"`\n          example.run\n        end\n      end\n\n      context 'when using one of multiple aliases' do\n        let(:command) { 'git amend' }\n\n        it { should == true }\n      end\n\n      context 'when using another of multiple aliases' do\n        let(:command) { 'git other-amend' }\n\n        it { should == true }\n      end\n    end\n\n    context 'when not amending a commit' do\n      context 'using `git commit`' do\n        let(:command) { 'git commit' }\n\n        it { should == false }\n      end\n\n      context 'using a git alias containing \"--amend\"' do\n        let(:command) { 'git no--amend' }\n\n        around do |example|\n          repo do\n            `git config alias.no--amend commit`\n            example.run\n          end\n        end\n\n        it { should == false }\n      end\n    end\n  end\n\n  describe '#setup_environment' do\n    subject { context.setup_environment }\n\n    context 'when there are no staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'other-tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'keeps already-committed files' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\n\"\n      end\n\n      it 'does not keep unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.should == \"Hello Other World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when there are staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'tracked-file', append: true)\n          echo('Some more text', 'other-tracked-file', append: true)\n          `git add tracked-file`\n          echo('Yet some more text', 'tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'keeps staged changes' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\nSome more text\\n\"\n      end\n\n      it 'does not keep unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.should == \"Hello Other World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when all changes have been staged' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          echo('Hello Other World', 'other-tracked-file')\n          `git add other-tracked-file`\n          example.run\n        end\n      end\n\n      it 'does not stash changes' do\n        expect(context.private_methods).to include :stash_changes\n        expect(context).not_to receive(:stash_changes)\n        subject\n      end\n    end\n\n    context 'when renaming a file during an amendment' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          touch 'some-file'\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          `git mv some-file renamed-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it 'does not try to update modification time of the old non-existent file' do\n        File.should_receive(:mtime).with(/renamed-file/)\n        File.should_not_receive(:mtime).with(/some-file/)\n        subject\n      end\n    end\n\n    context 'when only a submodule change is staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          `git add sub`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n    end\n\n    # Git cannot track Windows symlinks\n    unless Overcommit::OS.windows?\n      context 'when a broken symlink is staged' do\n        around do |example|\n          repo do\n            Overcommit::Utils::FileUtils.symlink('non-existent-file', 'symlink')\n            `git add symlink`\n            example.run\n          end\n        end\n\n        it 'does not attempt to update/restore the modification time of the file' do\n          File.should_not_receive(:mtime)\n          File.should_not_receive(:utime)\n          subject\n        end\n      end\n    end\n  end\n\n  describe '#cleanup_environment' do\n    subject { context.cleanup_environment }\n\n    before do\n      context.setup_environment\n    end\n\n    context 'when there were no staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'other-tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'restores the unstaged changes' do\n        subject\n        File.open('other-tracked-file', 'r').read.\n          should == \"Hello Other World\\nSome more text\\n\"\n      end\n\n      it 'keeps already-committed files' do\n        subject\n        File.open('tracked-file', 'r').read.should == \"Hello World\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when there were staged changes' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          echo('Hello Other World', 'other-tracked-file')\n          `git add tracked-file other-tracked-file`\n          `git commit -m \"Add tracked-file and other-tracked-file\"`\n          echo('Hello Again', 'untracked-file')\n          echo('Some more text', 'tracked-file', append: true)\n          echo('Some more text', 'other-tracked-file', append: true)\n          `git add tracked-file`\n          echo('Yet some more text', 'tracked-file', append: true)\n          example.run\n        end\n      end\n\n      it 'restores the unstaged changes' do\n        subject\n        File.open('tracked-file', 'r').read.\n          should == \"Hello World\\nSome more text\\nYet some more text\\n\"\n      end\n\n      it 'keeps staged changes' do\n        subject\n        `git show :tracked-file`.should == \"Hello World\\nSome more text\\n\"\n      end\n\n      it 'keeps untracked files' do\n        subject\n        File.open('untracked-file', 'r').read.should == \"Hello Again\\n\"\n      end\n\n      it 'keeps modification times the same' do\n        sleep 1\n        expect { subject }.to_not change {\n          [\n            File.mtime('tracked-file'),\n            File.mtime('other-tracked-file'),\n            File.mtime('untracked-file')\n          ]\n        }\n      end\n    end\n\n    context 'when all changes were staged' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          echo('Hello Other World', 'other-tracked-file')\n          `git add other-tracked-file`\n          example.run\n        end\n      end\n\n      it 'does not touch the working tree' do\n        expect(context.private_methods).to include :clear_working_tree\n        expect(context.private_methods).to include :restore_working_tree\n        expect(context).not_to receive(:clear_working_tree)\n        expect(context).not_to receive(:restore_working_tree)\n        subject\n      end\n    end\n\n    context 'when there were deleted files' do\n      around do |example|\n        repo do\n          echo('Hello World', 'tracked-file')\n          `git add tracked-file`\n          `git commit -m \"Add tracked-file\"`\n          `git rm tracked-file`\n          example.run\n        end\n      end\n\n      it 'deletes the file' do\n        subject\n        File.exist?('tracked-file').should == false\n      end\n    end\n\n    context 'when only a submodule change was staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          `git add sub`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n    end\n\n    context 'when submodule changes were staged along with other changes' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          echo('Hello World', 'sub/submodule-file')\n          `git submodule foreach \"git add submodule-file\" < #{File::NULL}`\n          `git submodule foreach \"git config --local commit.gpgsign false\"`\n          `git submodule foreach \"git commit -m \\\\\"Another commit\\\\\"\" < #{File::NULL}`\n          echo('Hello Again', 'tracked-file')\n          `git add sub tracked-file`\n          example.run\n        end\n      end\n\n      it 'keeps staged submodule change' do\n        `git config diff.submodule short`\n        expect { subject }.to_not change {\n          (`git diff --cached` =~ /-Subproject commit[\\s\\S]*\\+Subproject commit/).nil?\n        }.from(false)\n      end\n\n      it 'keeps staged file change' do\n        subject\n        `git show :tracked-file`.should == \"Hello Again\\n\"\n      end\n    end\n\n    context 'when a submodule removal was staged' do\n      around do |example|\n        submodule = repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n        end\n\n        repo do\n          `git -c protocol.file.allow=always submodule add #{submodule} sub > #{File::NULL} 2>&1`\n          `git commit -m \"Add submodule\"`\n          `git rm sub`\n          example.run\n        end\n      end\n\n      it 'does not leave behind an empty submodule directory' do\n        subject\n        File.exist?('sub').should == false\n      end\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    before do\n      context.stub(:amendment?).and_return(false)\n    end\n\n    it 'does not include submodules' do\n      submodule = repo do\n        touch 'foo'\n        `git add foo`\n        `git commit -m \"Initial commit\"`\n      end\n\n      repo do\n        `git -c protocol.file.allow=always submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n        expect(subject).to_not include File.expand_path('test-sub')\n      end\n    end\n\n    context 'when no files were staged' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when files were added' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were modified' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          echo('Hello', 'some-file')\n          `git add some-file`\n          example.run\n        end\n      end\n\n      it { should == [File.expand_path('some-file')] }\n    end\n\n    context 'when files were deleted' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          `git rm some-file`\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when amending last commit' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          `git commit -m \"Initial commit\"`\n          touch('other-file')\n          `git add other-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it { should =~ [File.expand_path('some-file'), File.expand_path('other-file')] }\n    end\n\n    context 'when renaming a file during an amendment' do\n      around do |example|\n        repo do\n          `git commit --allow-empty -m \"Initial commit\"`\n          touch 'some-file'\n          `git add some-file`\n          `git commit -m \"Add file\"`\n          `git mv some-file renamed-file`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it 'does not include the old file name in the list of modified files' do\n        subject.should_not include File.expand_path('some-file')\n      end\n    end\n\n    # Git cannot track Windows symlinks\n    unless Overcommit::OS.windows?\n      context 'when changing a symlink to a directory during an amendment' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            FileUtils.mkdir 'some-directory'\n            symlink('some-directory', 'some-symlink')\n            `git add some-symlink some-directory`\n            `git commit -m \"Add file\"`\n            `git rm some-symlink`\n            FileUtils.mkdir 'some-symlink'\n            touch File.join('some-symlink', 'another-file')\n            `git add some-symlink`\n            example.run\n          end\n        end\n\n        before do\n          context.stub(:amendment?).and_return(true)\n        end\n\n        it 'does not include the directory in the list of modified files' do\n          subject.should_not include File.expand_path('some-symlink')\n        end\n      end\n\n      context 'when breaking a symlink during an amendment' do\n        around do |example|\n          repo do\n            `git commit --allow-empty -m \"Initial commit\"`\n            FileUtils.mkdir 'some-directory'\n            touch File.join('some-directory', 'some-file')\n            symlink('some-directory', 'some-symlink')\n            `git add some-symlink some-directory`\n            `git commit -m \"Add file\"`\n            `git rm -rf some-directory`\n            example.run\n          end\n        end\n\n        before do\n          context.stub(:amendment?).and_return(true)\n        end\n\n        it 'still includes the broken symlink in the list of modified files' do\n          subject.should include File.expand_path('some-symlink')\n        end\n      end\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    before do\n      context.stub(:amendment?).and_return(false)\n    end\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when amending last commit' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          File.open(modified_file, 'a') { |f| f.puts 4 }\n          `git add #{modified_file}`\n          example.run\n        end\n      end\n\n      before do\n        context.stub(:amendment?).and_return(true)\n      end\n\n      it { should == Set.new(1..4) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/pre_push_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/pre_push'\n\ndescribe Overcommit::HookContext::PrePush do\n  let(:config) { double('config') }\n  let(:args) { [remote_name, remote_url] }\n  let(:input) { double('input') }\n  let(:remote_name) { 'origin' }\n  let(:remote_url) { 'git@github.com:brigade/overcommit.git' }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#remote_name' do\n    subject { context.remote_name }\n\n    it { should == remote_name }\n  end\n\n  describe '#remote_url' do\n    subject { context.remote_url }\n\n    it { should == remote_url }\n  end\n\n  describe '#remote_ref_deletion?' do\n    subject { context.remote_ref_deletion? }\n\n    let(:standard_input) { \"#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\\n\" }\n\n    before do\n      input.stub(:read).and_return(standard_input)\n    end\n\n    context 'when pushing new branch to remote ref' do\n      let(:local_ref) { 'refs/heads/test' }\n      let(:local_sha1) { '' }\n      let(:remote_ref) { 'refs/heads/test' }\n      let(:remote_sha1) { '0' * 40 }\n\n      it { should == false }\n    end\n\n    context 'when pushing update to remote ref' do\n      let(:local_ref) { 'refs/heads/test' }\n      let(:local_sha1) { '' }\n      let(:remote_ref) { 'refs/heads/test' }\n      let(:remote_sha1) { random_hash }\n\n      it { should == false }\n    end\n\n    context 'when deleting remote ref' do\n      let(:local_ref) { '(deleted)' }\n      let(:local_sha1) { '' }\n      let(:remote_ref) { 'refs/heads/test' }\n      let(:remote_sha1) { random_hash }\n\n      it { should == true }\n    end\n\n    context 'when no standard input is provided' do\n      let(:standard_input) { '' }\n\n      it { should == false }\n    end\n  end\n\n  describe '#pushed_refs' do\n    subject(:pushed_refs) { context.pushed_refs }\n\n    let(:local_ref) { 'refs/heads/master' }\n    let(:local_sha1) { random_hash }\n    let(:remote_ref) { 'refs/heads/master' }\n    let(:remote_sha1) { random_hash }\n\n    before do\n      input.stub(:read).and_return(\"#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\\n\")\n    end\n\n    it 'parses commit info from the input' do\n      pushed_refs.length.should == 1\n      pushed_refs.each do |pushed_ref|\n        pushed_ref.local_ref.should == local_ref\n        pushed_ref.local_sha1.should == local_sha1\n        pushed_ref.remote_ref.should == remote_ref\n        pushed_ref.remote_sha1.should == remote_sha1\n      end\n    end\n  end\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    let(:remote_repo) do\n      repo do\n        touch 'update-me'\n        echo 'update', 'update-me'\n        touch 'delete-me'\n        echo 'delete', 'delete-me'\n        `git add . 2>&1 > #{File::NULL}`\n        `git commit -m \"Initial commit\" 2>&1 > #{File::NULL}`\n      end\n    end\n\n    context 'when current branch has tracking branch' do\n      let(:local_ref) { 'refs/heads/project-branch' }\n      let(:local_sha1) { get_sha1(local_ref) }\n      let(:remote_ref) { 'refs/remotes/origin/master' }\n      let(:remote_sha1) { get_sha1(remote_ref) }\n      let(:input) do\n        double('input', read: \"#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\\n\")\n      end\n\n      it 'has modified files based on tracking branch' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch 2>&1 > #{File::NULL}`\n\n          touch 'added-1'\n          echo 'add', 'added-1'\n          echo 'append', 'update-me'\n          FileUtils.rm 'delete-me'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1\" 2>&1 > #{File::NULL}`\n\n          touch 'added-2'\n          echo 'add', 'added-2'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 2\" 2>&1 > #{File::NULL}`\n\n          should == %w[added-1 added-2 update-me].map { |file| File.expand_path(file) }\n          should_not include(*%w[delete-me].map { |file| File.expand_path(file) })\n        end\n      end\n    end\n\n    context 'when pushing multiple branches at once' do\n      let(:local_ref_1) { 'refs/heads/project-branch-1' }\n      let(:local_sha1_1) { get_sha1(local_ref_1) }\n      let(:local_ref_2) { 'refs/heads/project-branch-2' }\n      let(:local_sha1_2) { get_sha1(local_ref_2) }\n      let(:remote_ref) { 'refs/remotes/origin/master' }\n      let(:remote_sha1) { get_sha1(remote_ref) }\n      let(:input) do\n        double('input', read: ref_ranges)\n      end\n      let(:ref_ranges) do\n        [\n          \"#{local_ref_1} #{local_sha1_1} #{remote_ref} #{remote_sha1}\\n\",\n          \"#{local_ref_2} #{local_sha1_2} #{remote_ref} #{remote_sha1}\\n\"\n        ].join\n      end\n\n      it 'has modified files based on multiple tracking branches' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch-1 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-1 2>&1 > #{File::NULL}`\n\n          touch 'added-1'\n          echo 'add', 'added-1'\n          echo 'append', 'update-me'\n          FileUtils.rm 'delete-me'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1\" 2>&1 > #{File::NULL}`\n\n          `git checkout master 2>&1 > #{File::NULL}`\n          `git checkout -b project-branch-2 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-2 2>&1 > #{File::NULL}`\n\n          echo 'append', 'update-me'\n          touch 'added-2'\n          echo 'add', 'added-2'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 2\" 2>&1 > #{File::NULL}`\n\n          should == %w[added-1 update-me added-2].map { |file| File.expand_path(file) }\n          should_not include(*%w[delete-me].map { |file| File.expand_path(file) })\n        end\n      end\n    end\n\n    context 'when current branch has no tracking branch' do\n      let(:local_ref) { 'refs/heads/project-branch' }\n      let(:local_sha1) { get_sha1(local_ref) }\n      let(:remote_ref) { 'refs/heads/master' }\n      let(:remote_sha1) { get_sha1(remote_ref) }\n      let(:input) do\n        double('input', read: \"#{local_ref} #{local_sha1} #{remote_ref} #{remote_sha1}\\n\")\n      end\n\n      it 'has modified files based on parent branch' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch 2>&1 > #{File::NULL}`\n\n          touch 'added-1'\n          echo 'add', 'added-1'\n          echo 'append', 'update-me'\n          FileUtils.rm 'delete-me'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1\" 2>&1 > #{File::NULL}`\n\n          touch 'added-2'\n          echo 'add', 'added-2'\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 2\" 2>&1 > #{File::NULL}`\n\n          should == %w[added-1 added-2 update-me].map { |file| File.expand_path(file) }\n          should_not include(*%w[delete-me].map { |file| File.expand_path(file) })\n        end\n      end\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    subject { context.modified_lines_in_file(file) }\n    let(:local_ref_1) { 'refs/heads/project-branch-1' }\n    let(:local_sha1_1) { get_sha1(local_ref_1) }\n    let(:local_ref_2) { 'refs/heads/project-branch-2' }\n    let(:local_sha1_2) { get_sha1(local_ref_2) }\n    let(:remote_ref) { 'refs/remotes/origin/master' }\n    let(:remote_sha1) { get_sha1(remote_ref) }\n    let(:input) do\n      double('input', read: ref_ranges)\n    end\n    let(:ref_ranges) do\n      [\n        \"#{local_ref_1} #{local_sha1_1} #{remote_ref} #{remote_sha1}\\n\",\n        \"#{local_ref_2} #{local_sha1_2} #{remote_ref} #{remote_sha1}\\n\"\n      ].join\n    end\n    let(:remote_repo) do\n      repo do\n        touch 'initial_file'\n        echo 'initial', 'initial_file'\n        `git add . 2>&1 > #{File::NULL}`\n        `git commit -m \"Initial commit\" 2>&1 > #{File::NULL}`\n      end\n    end\n\n    context 'when updating a file' do\n      let(:file) { File.expand_path('initial_file') }\n\n      it 'has modified lines in file' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch-1 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-1 2>&1 > #{File::NULL}`\n\n          echo 'append-1', 'initial_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1 Commit 1\" 2>&1 > #{File::NULL}`\n\n          echo 'append-2', 'initial_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1 Commit 2\" 2>&1 > #{File::NULL}`\n\n          `git checkout -b project-branch-2 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-2 2>&1 > #{File::NULL}`\n\n          echo 'append-3', 'initial_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 2 Commit 1\" 2>&1 > #{File::NULL}`\n\n          should == [2, 3, 4].to_set\n        end\n      end\n    end\n\n    context 'when adding a file' do\n      let(:file) { File.expand_path('new_file') }\n\n      it 'has modified lines in file' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch-1 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-1 2>&1 > #{File::NULL}`\n\n          touch 'new_file'\n\n          echo 'append-1', 'new_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1 Commit 1\" 2>&1 > #{File::NULL}`\n\n          echo 'append-2', 'new_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1 Commit 2\" 2>&1 > #{File::NULL}`\n\n          `git checkout -b project-branch-2 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-2 2>&1 > #{File::NULL}`\n\n          echo 'append-3', 'new_file', append: true\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 2 Commit 1\" 2>&1 > #{File::NULL}`\n\n          should == [1, 2, 3].to_set\n        end\n      end\n    end\n\n    context 'when deleting a file' do\n      let(:file) { File.expand_path('initial_file') }\n      let(:ref_ranges) do\n        \"#{local_ref_1} #{local_sha1_1} #{remote_ref} #{remote_sha1}\\n\"\n      end\n\n      it 'has modified lines in file' do\n        repo do\n          `git remote add origin file://#{remote_repo}`\n          `git fetch origin 2>&1 > #{File::NULL} && git reset --hard origin/master`\n\n          `git checkout -b project-branch-1 2>&1 > #{File::NULL}`\n          `git push -u origin project-branch-1 2>&1 > #{File::NULL}`\n\n          FileUtils.rm 'initial_file'\n\n          `git add . 2>&1 > #{File::NULL}`\n          `git commit -m \"Update Branch 1\" 2>&1 > #{File::NULL}`\n\n          should == [].to_set\n        end\n      end\n    end\n  end\n\n  describe Overcommit::HookContext::PrePush::PushedRef do\n    let(:local_ref) { 'refs/heads/master' }\n    let(:remote_ref) { 'refs/heads/master' }\n    let(:local_sha1) { random_hash }\n    let(:remote_sha1) { random_hash }\n    let(:pushed_ref) { described_class.new(local_ref, local_sha1, remote_ref, remote_sha1) }\n\n    describe '#forced?' do\n      subject { pushed_ref.forced? }\n\n      context 'when creating a ref' do\n        before do\n          pushed_ref.stub(created?: true, deleted?: false)\n        end\n\n        it { should == false }\n      end\n\n      context 'when deleting a ref' do\n        before do\n          pushed_ref.stub(created?: false, deleted?: true)\n        end\n\n        it { should == false }\n      end\n\n      context 'when remote commits are not overwritten' do\n        before do\n          pushed_ref.stub(created?: false,\n                          deleted?: false,\n                          overwritten_commits: [])\n        end\n\n        it { should == false }\n      end\n\n      context 'when remote commits are overwritten' do\n        before do\n          pushed_ref.stub(created?: false,\n                          deleted?: false,\n                          overwritten_commits: [random_hash])\n        end\n\n        it { should == true }\n      end\n\n      context 'when remote ref head does not exist locally' do\n        let(:git_error_msg) { \"fatal: bad object #{remote_sha1}\" }\n\n        before do\n          pushed_ref.stub(created?: false, deleted?: false)\n          result = double(success?: false, stderr: git_error_msg)\n          Overcommit::Subprocess.stub(:spawn).and_return(result)\n        end\n\n        it 'should raise' do\n          expect { subject }.to raise_error(Overcommit::Exceptions::GitRevListError,\n                                            /#{git_error_msg}/)\n        end\n      end\n    end\n\n    describe '#created?' do\n      subject { pushed_ref.created? }\n\n      context 'when creating a ref' do\n        before do\n          pushed_ref.stub(:remote_sha1).and_return('0' * 40)\n        end\n\n        it { should == true }\n      end\n\n      context 'when not creating a ref' do\n        before do\n          pushed_ref.stub(:remote_sha1).and_return(random_hash)\n        end\n\n        it { should == false }\n      end\n    end\n\n    describe '#deleted?' do\n      subject { pushed_ref.deleted? }\n\n      context 'when deleting a ref' do\n        before do\n          pushed_ref.stub(:local_sha1).and_return('0' * 40)\n        end\n\n        it { should == true }\n      end\n\n      context 'when not deleting a ref' do\n        before do\n          pushed_ref.stub(:local_sha1).and_return(random_hash)\n        end\n\n        it { should == false }\n      end\n    end\n\n    describe '#destructive?' do\n      subject { pushed_ref.destructive? }\n\n      context 'when deleting a ref' do\n        before do\n          pushed_ref.stub(:deleted?).and_return(true)\n        end\n\n        it { should == true }\n      end\n\n      context 'when force-pushing a ref' do\n        before do\n          pushed_ref.stub(deleted?: false, forced?: true)\n        end\n\n        it { should == true }\n      end\n\n      context 'when not deleting or force-pushing a ref' do\n        before do\n          pushed_ref.stub(deleted?: false, forced?: false)\n        end\n\n        it { should == false }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/pre_rebase_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/pre_rebase'\n\ndescribe Overcommit::HookContext::PreRebase do\n  let(:config) { double('config') }\n  let(:args) { [upstream_branch, rebased_branch] }\n  let(:upstream_branch) { 'master' }\n  let(:rebased_branch) { 'topic' }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#upstream_branch' do\n    subject { context.upstream_branch }\n\n    it { should == upstream_branch }\n  end\n\n  describe '#rebased_branch' do\n    subject { context.rebased_branch }\n\n    it { should == rebased_branch }\n\n    context 'when rebasing current branch' do\n      let(:rebased_branch) { nil }\n      let(:current_branch) { 'master' }\n\n      around do |example|\n        repo do\n          `git checkout -b #{current_branch} > #{File::NULL} 2>&1`\n          example.run\n        end\n      end\n\n      it { should == current_branch }\n    end\n  end\n\n  describe '#fast_forward?' do\n    subject { context.fast_forward? }\n\n    context 'when upstream branch is descendent from rebased branch' do\n      before do\n        context.stub(:rebased_commits).and_return([])\n      end\n\n      it { should == true }\n    end\n\n    context 'when upstream branch is not descendent from rebased branch' do\n      before do\n        context.stub(:rebased_commits).and_return([random_hash])\n      end\n\n      it { should == false }\n    end\n  end\n\n  describe '#detached_head?' do\n    subject { context.detached_head? }\n\n    context 'when rebasing a detached HEAD' do\n      let(:rebased_branch) { '' }\n\n      it { should == true }\n    end\n\n    context 'when rebasing a branch' do\n      let(:rebased_branch) { 'topic' }\n\n      it { should == false }\n    end\n  end\n\n  describe '#rebased_commits' do\n    subject { context.rebased_commits }\n\n    let(:base_branch) { 'master' }\n    let(:topic_branch_1) { 'topic-1' }\n    let(:topic_branch_2) { 'topic-2' }\n\n    around do |example|\n      repo do\n        `git checkout -b #{base_branch} > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Initial Commit\"`\n        `git checkout -b #{topic_branch_1} > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Hello World\"`\n        `git checkout -b #{topic_branch_2} #{base_branch} > #{File::NULL} 2>&1`\n        `git commit --allow-empty -m \"Hello Again\"`\n        example.run\n      end\n    end\n\n    context 'when upstream branch is descendent from rebased branch' do\n      let(:upstream_branch) { topic_branch_1 }\n      let(:rebased_branch) { base_branch }\n\n      it { should be_empty }\n    end\n\n    context 'when upstream branch is not descendent from rebased branch' do\n      let(:upstream_branch) { topic_branch_1 }\n      let(:rebased_branch) { topic_branch_2 }\n\n      it { should_not be_empty }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/prepare_commit_msg_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/prepare_commit_msg'\n\ndescribe Overcommit::HookContext::PrepareCommitMsg do\n  let(:config) { double('config') }\n  let(:args) { [commit_message_filename, commit_message_source] }\n  let(:commit_message_filename) { 'message-template.txt' }\n  let(:commit_message_source) { :file }\n  let(:commit) { 'SHA-1 here' }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#commit_message_filename' do\n    subject { context.commit_message_filename }\n\n    it { should == commit_message_filename }\n  end\n\n  describe '#commit_message_source' do\n    subject { context.commit_message_source }\n\n    it { should == commit_message_source }\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_context/run_all_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'overcommit/hook_context/run_all'\n\ndescribe Overcommit::HookContext::RunAll do\n  let(:config) { double('config') }\n  let(:args) { [] }\n  let(:input) { double('input') }\n  let(:context) { described_class.new(config, args, input) }\n\n  describe '#modified_files' do\n    subject { context.modified_files }\n\n    context 'when repo contains no files' do\n      around do |example|\n        repo do\n          example.run\n        end\n      end\n\n      it { should be_empty }\n    end\n\n    context 'when repo contains files' do\n      around do |example|\n        repo do\n          touch('some-file')\n          `git add some-file`\n          touch('some-other-file')\n          `git add some-other-file`\n          `git commit -m \"Add files\"`\n          example.run\n        end\n      end\n\n      it { should == %w[some-file some-other-file].map { |file| File.expand_path(file) } }\n    end\n\n    context 'when repo contains submodules' do\n      around do |example|\n        submodule = repo do\n          touch 'foo'\n          `git add foo`\n          `git commit -m \"Initial commit\"`\n        end\n\n        repo do\n          `git submodule add #{submodule} test-sub 2>&1 > #{File::NULL}`\n          example.run\n        end\n      end\n\n      it { should_not include File.expand_path('test-sub') }\n    end\n  end\n\n  describe '#modified_lines_in_file' do\n    let(:modified_file) { 'some-file' }\n    subject { context.modified_lines_in_file(modified_file) }\n\n    context 'when file contains a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') { |f| (1..3).each { |i| f.write(\"#{i}\\n\") } }\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n\n    context 'when file does not contain a trailing newline' do\n      around do |example|\n        repo do\n          File.open(modified_file, 'w') do |f|\n            (1..2).each { |i| f.write(\"#{i}\\n\") }\n            f.write(3)\n          end\n\n          `git add #{modified_file}`\n          `git commit -m \"Add files\"`\n          example.run\n        end\n      end\n\n      it { should == Set.new(1..3) }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/hook_signer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::HookSigner do\n  describe '#signature_changed?' do\n    let(:config) { double('config') }\n    let(:context) { double('context') }\n\n    let(:signer) do\n      described_class.new('.git-hooks/pre_commit/some_path.rb', config, context)\n    end\n\n    let(:hook_config) { { 'enabled' => false } }\n    let(:modified_hook_config) { hook_config }\n\n    let(:hook_contents) { <<-RUBY }\n      module Overcommit::Hook::PreCommit\n        class SomeHook\n          def run\n            :pass\n          end\n        end\n      end\n    RUBY\n\n    let(:modified_hook_contents) { hook_contents }\n\n    subject { signer.signature_changed? }\n\n    around do |example|\n      repo do\n        example.run\n      end\n    end\n\n    before do\n      Overcommit::GitRepo.stub(:tracked?).and_return(true)\n      context.stub(:hook_class_name).and_return('PreCommit')\n      context.stub(:hook_type_name).and_return('pre-commit')\n      config.stub(:verify_signatures?).and_return(true)\n      config.stub(:for_hook).and_return(hook_config)\n      config.stub(:plugin_directory).and_return(Dir.pwd)\n      signer.stub(:hook_contents).and_return(hook_contents)\n\n      signer.update_signature!\n\n      config.stub(:for_hook).and_return(modified_hook_config)\n      signer.stub(:hook_contents).and_return(modified_hook_contents)\n    end\n\n    context 'when the hook code and config are the same' do\n      it { should == false }\n\n      context 'and the user has specified they wish to skip the hook' do\n        let(:modified_hook_config) { hook_config.merge('skip' => true) }\n\n        it { should == false }\n      end\n    end\n\n    context 'when the hook code has changed' do\n      let(:modified_hook_contents) { <<-RUBY }\n        module Overcommit::Hook::PreCommit\n          class SomeHook\n            def run\n              :fail # This line changed\n            end\n          end\n        end\n      RUBY\n\n      it { should == true }\n    end\n\n    context 'when the hook config has changed' do\n      let(:modified_hook_config) { { 'enabled' => true } }\n\n      it { should == true }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/installer_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Installer do\n  let(:logger) { Overcommit::Logger.silent }\n  let(:installer) { described_class.new(logger) }\n\n  def hook_files_installed?(hooks_dir)\n    Overcommit::Utils.supported_hook_types.all? do |hook_type|\n      hook_file = File.join(hooks_dir, hook_type)\n      master_hook = File.join(hooks_dir, 'overcommit-hook')\n      File.exist?(hook_file) && File.exist?(master_hook) &&\n        File.read(hook_file) == File.read(master_hook)\n    end\n  end\n\n  describe '#run' do\n    let(:options) { { action: :install } }\n    subject { installer.run(target.to_s, options) }\n\n    context 'when the target is not a directory' do\n      let(:target) { Tempfile.new('some-file') }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidGitRepo\n      end\n    end\n\n    context 'when the target is not a git repo' do\n      let(:target) { directory }\n\n      it 'raises an error' do\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidGitRepo\n      end\n    end\n\n    context 'when the target is a git repo' do\n      let(:target) { repo }\n      let(:hooks_dir) { File.join(target, '.git', 'hooks') }\n      let(:old_hooks_dir) { File.join(hooks_dir, 'old-hooks') }\n      let(:master_hook) { File.join(hooks_dir, 'overcommit-hook') }\n\n      context 'and an install is requested' do\n        context 'and Overcommit hooks were not previously installed' do\n          it 'installs the master hook into the hooks directory' do\n            expect { subject }.to change {\n              File.file?(master_hook)\n            }.from(false).to(true)\n          end\n\n          it 'copies all supported hooks from the master hook' do\n            expect { subject }.to change {\n              hook_files_installed?(hooks_dir)\n            }.from(false).to(true)\n          end\n        end\n\n        context 'and Overcommit hooks were previously installed' do\n          before do\n            installer.run(target, action: :install)\n          end\n\n          it 'keeps the master hook' do\n            expect { subject }.to_not change {\n              File.file?(master_hook)\n            }.from(true)\n          end\n\n          it 'maintains all copies of the master hook' do\n            expect { subject }.to_not change {\n              hook_files_installed?(hooks_dir)\n            }.from(true)\n          end\n        end\n\n        context 'and non-Overcommit hooks were previously installed' do\n          let(:old_hooks) { %w[commit-msg pre-commit] }\n\n          before do\n            FileUtils.mkdir_p(hooks_dir)\n            Dir.chdir(hooks_dir) do\n              old_hooks.each { |hook_type| touch(hook_type) }\n            end\n          end\n\n          it 'does not raise an error' do\n            expect { subject }.to_not raise_error\n          end\n\n          it 'moves them to a subdirectory' do\n            expect { subject }.to change {\n              old_hooks.all? do |hook_type|\n                File.exist?(File.join(old_hooks_dir, hook_type))\n              end\n            }.from(false).to(true)\n          end\n\n          context 'and the force option is specified' do\n            let(:options) { super().merge(force: true) }\n\n            it 'does not raise an error' do\n              expect { subject }.to_not raise_error\n            end\n\n            it 'copies all supported hooks from the master hook' do\n              expect { subject }.to change {\n                hook_files_installed?(hooks_dir)\n              }.from(false).to(true)\n            end\n          end\n        end\n\n        context 'and a repo configuration file is already present' do\n          let(:existing_content) { '# Hello World' }\n\n          around do |example|\n            Dir.chdir(target) do\n              File.open('.overcommit.yml', 'w') { |f| f.write(existing_content) }\n              example.run\n            end\n          end\n\n          it 'does not overwrite the existing configuration' do\n            expect { subject }.to_not change {\n              File.open('.overcommit.yml').read\n            }.from(existing_content)\n          end\n        end\n\n        context 'and a repo configuration file is not present' do\n          around do |example|\n            Dir.chdir(target) do\n              example.run\n            end\n          end\n\n          it 'creates a starter configuration file' do\n            expect { subject }.to change {\n              File.exist?('.overcommit.yml') && FileUtils.compare_file(\n                '.overcommit.yml',\n                File.join(Overcommit::HOME, 'config', 'starter.yml')\n              )\n            }.from(false).to(true)\n          end\n        end\n\n        context 'and a custom core.hooksPath directory is set' do\n          around do |example|\n            Dir.chdir(target) do\n              FileUtils.mkdir 'my-hooks'\n              `git config core.hooksPath my-hooks`\n              example.run\n            end\n          end\n\n          it 'installs the hooks in the custom directory' do\n            expect { subject }.to change { hook_files_installed?(File.join(target, 'my-hooks')) }.\n                               from(false).\n                               to(true)\n          end\n        end\n      end\n\n      context 'and an uninstall is requested' do\n        let(:options) { { action: :uninstall } }\n\n        context 'and Overcommit hooks were previously installed' do\n          before do\n            installer.run(target, action: :install)\n          end\n\n          it 'removes the master hook from the hooks directory' do\n            expect { subject }.to change {\n              File.exist?(master_hook)\n            }.from(true).to(false)\n          end\n\n          it 'removes all hook files from the hooks directory' do\n            expect { subject }.to change {\n              hook_files_installed?(hooks_dir)\n            }.from(true).to(false)\n          end\n        end\n\n        context 'and Overcommit hooks were not previously installed' do\n          it 'does not raise an error' do\n            expect { subject }.to_not raise_error\n          end\n        end\n\n        context 'and non-Overcommit hooks were previously installed' do\n          let(:old_hooks) { %w[commit-msg pre-commit] }\n\n          context 'before installing Overcommit hooks' do\n            before do\n              FileUtils.mkdir_p(old_hooks_dir)\n              Dir.chdir(old_hooks_dir) do\n                old_hooks.each { |hook_type| touch(hook_type) }\n              end\n            end\n\n            it 'restores the previously existing hooks' do\n              expect { subject }.to change {\n                old_hooks.all? do |hook_type|\n                  File.exist?(File.join(hooks_dir, hook_type))\n                end\n              }.from(false).to(true)\n            end\n          end\n\n          context 'after installing Overcommit hooks' do\n            before do\n              FileUtils.mkdir_p(hooks_dir)\n              Dir.chdir(hooks_dir) do\n                old_hooks.each { |hook_type| touch(hook_type) }\n              end\n            end\n\n            it 'does not remove the previously existing hooks' do\n              expect { subject }.to_not change {\n                old_hooks.all? do |hook_type|\n                  File.exist?(File.join(hooks_dir, hook_type))\n                end\n              }.from(true)\n            end\n          end\n        end\n      end\n\n      context 'which has an external git dir' do\n        let(:submodule) { File.join(target, 'submodule') }\n        before do\n          system 'git', '-c', 'protocol.file.allow=always', 'submodule', 'add', target,\n                 'submodule', chdir: target, out: :close, err: :close\n        end\n        let(:submodule_git_file) { File.join(submodule, '.git') }\n        let(:submodule_git_dir) do\n          File.expand_path(File.read(submodule_git_file)[/gitdir: (.*)/, 1], submodule)\n        end\n        let(:submodule_hooks_dir) { File.join(submodule_git_dir, 'hooks') }\n\n        subject { installer.run(submodule, options) }\n\n        it 'installs hooks into the correct external directory' do\n          expect { subject }.to change {\n            hook_files_installed?(submodule_hooks_dir)\n          }.from(false).to(true)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/logger_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::Logger do\n  let(:io) { StringIO.new }\n  let(:output) { io.string }\n  subject { described_class.new(io) }\n\n  describe '.silent' do\n    subject { described_class.silent }\n\n    it 'does not output anything' do\n      capture_stdout { subject.log('Something') }.should be_empty\n    end\n  end\n\n  describe '#partial' do\n    subject { super().partial('Hello') }\n\n    it 'writes to the output stream' do\n      subject\n      output.should_not be_empty\n    end\n\n    it 'does not append a newline' do\n      subject\n      output[-1].should_not == \"\\n\"\n    end\n  end\n\n  describe '#log' do\n    subject { super().log('Hello') }\n\n    it 'writes to the output stream' do\n      subject\n      output.should_not be_empty\n    end\n\n    it 'appends a newline' do\n      subject\n      output[-1, 1].should == \"\\n\"\n    end\n  end\n\n  shared_examples_for 'colorized output' do\n    subject { super().send(method, 'Hello') }\n\n    it 'writes to the output stream' do\n      subject\n      output.should_not be_empty\n    end\n\n    it 'appends a newline' do\n      subject\n      output[-1, 1].should == \"\\n\"\n    end\n\n    context 'when the output stream is a TTY' do\n      before do\n        io.stub(:tty?).and_return(true)\n      end\n\n      it 'includes the color escape sequence' do\n        subject\n        output.should include \"\\033[#{color_code}m\"\n      end\n\n      it 'ends with the color reset sequence' do\n        subject\n        output.should end_with \"[0m\\n\"\n      end\n\n      context 'and colorization is disabled' do\n        around do |example|\n          Overcommit::Utils.with_environment 'OVERCOMMIT_COLOR' => '0' do\n            example.run\n          end\n        end\n\n        it 'omits the color escape sequence' do\n          subject\n          output.should_not include \"\\033\"\n        end\n      end\n    end\n\n    context 'when the output stream is not a TTY' do\n      before do\n        io.stub(:tty?).and_return(false)\n      end\n\n      it 'omits the color escape sequence' do\n        subject\n        output.should_not include \"\\033\"\n      end\n\n      context 'and colorization is enabled' do\n        around do |example|\n          Overcommit::Utils.with_environment 'OVERCOMMIT_COLOR' => '1' do\n            example.run\n          end\n        end\n\n        it 'includes the color escape sequence' do\n          subject\n          output.should include \"\\033[#{color_code}m\"\n        end\n\n        it 'ends with the color reset sequence' do\n          subject\n          output.should end_with \"[0m\\n\"\n        end\n      end\n    end\n  end\n\n  describe '#debug' do\n    context 'when debug mode is enabled' do\n      around do |example|\n        Overcommit::Utils.with_environment 'OVERCOMMIT_DEBUG' => '1' do\n          example.run\n        end\n      end\n\n      it_behaves_like 'colorized output' do\n        let(:method) { :debug }\n        let(:color_code) { '35' }\n      end\n    end\n\n    context 'when debug mode is not enabled' do\n      subject { super().debug('Hello') }\n\n      it 'does not write to the output stream' do\n        subject\n        output.should be_empty\n      end\n    end\n  end\n\n  describe '#bold' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :bold }\n      let(:color_code) { '1' }\n    end\n  end\n\n  describe '#error' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :error }\n      let(:color_code) { 31 }\n    end\n  end\n\n  describe '#bold_error' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :bold_error }\n      let(:color_code) { '1;31' }\n    end\n  end\n\n  describe '#success' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :success }\n      let(:color_code) { 32 }\n    end\n  end\n\n  describe '#warning' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :warning }\n      let(:color_code) { 33 }\n    end\n  end\n\n  describe '#bold_warning' do\n    it_behaves_like 'colorized output' do\n      let(:method) { :bold_warning }\n      let(:color_code) { '1;33' }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/message_processor_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\n\ndescribe Overcommit::MessageProcessor do\n  # Shorthand to make writing these tests a little more sane\n  EMH = Overcommit::MessageProcessor::ERRORS_MODIFIED_HEADER + \"\\n\"\n  WMH = Overcommit::MessageProcessor::WARNINGS_MODIFIED_HEADER + \"\\n\"\n  EUH = Overcommit::MessageProcessor::ERRORS_UNMODIFIED_HEADER + \"\\n\"\n  WUH = Overcommit::MessageProcessor::WARNINGS_UNMODIFIED_HEADER + \"\\n\"\n  EGH = Overcommit::MessageProcessor::ERRORS_GENERIC_HEADER + \"\\n\"\n  WGH = Overcommit::MessageProcessor::WARNINGS_GENERIC_HEADER + \"\\n\"\n\n  let(:config) { double('config') }\n  let(:context) { double('context') }\n  let(:hook) { Class.new(Overcommit::Hook::Base).new(config, context) }\n\n  subject { described_class.new(hook, setting) }\n\n  describe '#hook_result' do\n    let(:modified_lines) { {} }\n    subject { super().hook_result(messages) }\n\n    before do\n      config.stub(:for_hook).and_return({})\n\n      modified_lines.each do |file, lines|\n        hook.stub(:modified_lines_in_file).\n             with(file).\n             and_return(lines.to_set)\n      end\n    end\n\n    def error(file = nil, line = nil)\n      Overcommit::Hook::Message.new(:error, file, line, 'Error')\n    end\n\n    def warning(file = nil, line = nil)\n      Overcommit::Hook::Message.new(:warning, file, line, 'Warning')\n    end\n\n    context 'when there are no messages' do\n      let(:messages) { [] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:pass, ''] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:pass, ''] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:pass, ''] }\n      end\n    end\n\n    context 'when there are errors on modified lines' do\n      let(:modified_lines) { { 'a.txt' => [2] } }\n      let(:messages) { [error('a.txt', 2)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:fail, \"#{EMH}Error\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:fail, \"#{EMH}Error\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:fail, \"#{EMH}Error\\n\"] }\n      end\n    end\n\n    context 'when there are errors on unmodified lines' do\n      let(:modified_lines) { { 'a.txt' => [3] } }\n      let(:messages) { [error('a.txt', 2)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:fail, \"#{EUH}Error\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:warn, \"#{EUH}Error\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:pass, ''] }\n      end\n    end\n\n    context 'when there are warnings on modified lines' do\n      let(:modified_lines) { { 'a.txt' => [2] } }\n      let(:messages) { [warning('a.txt', 2)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:warn, \"#{WMH}Warning\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:warn, \"#{WMH}Warning\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:warn, \"#{WMH}Warning\\n\"] }\n      end\n    end\n\n    context 'when there are warnings on unmodified lines' do\n      let(:modified_lines) { { 'a.txt' => [3] } }\n      let(:messages) { [warning('a.txt', 2)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:warn, \"#{WUH}Warning\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:warn, \"#{WUH}Warning\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:pass, ''] }\n      end\n    end\n\n    context 'when there are errors and warnings on modified lines' do\n      let(:modified_lines) { { 'a.txt' => [2], 'b.txt' => [3, 4] } }\n      let(:messages) { [warning('a.txt', 2), error('b.txt', 4)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\"] }\n      end\n    end\n\n    context 'when there are errors and warnings on unmodified lines' do\n      let(:modified_lines) { { 'a.txt' => [2], 'b.txt' => [3, 4] } }\n      let(:messages) { [warning('a.txt', 3), error('b.txt', 5)] }\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n        it { should == [:fail, \"#{EUH}Error\\n#{WUH}Warning\\n\"] }\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n        it { should == [:warn, \"#{EUH}Error\\n#{WUH}Warning\\n\"] }\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:pass, ''] }\n      end\n    end\n\n    context 'when there are errors and warnings on modified/unmodified lines' do\n      let(:modified_lines) { { 'a.txt' => [2], 'b.txt' => [3, 4] } }\n\n      let(:messages) do\n        [\n          warning('a.txt', 3),\n          warning('b.txt', 3),\n          error('a.txt', 2),\n          error('b.txt', 5),\n        ]\n      end\n\n      context 'and setting is `report`' do\n        let(:setting) { 'report' }\n\n        it do\n          should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n\n      context 'and setting is `warn`' do\n        let(:setting) { 'warn' }\n\n        it do\n          should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n\n      context 'and setting is `ignore`' do\n        let(:setting) { 'ignore' }\n        it { should == [:fail, \"#{EMH}Error\\n#{WMH}Warning\\n\"] }\n      end\n    end\n\n    context 'when there are generic errors' do\n      let(:messages) { [error] * 2 }\n      let(:setting) { 'report' }\n\n      it { should == [:fail, \"Error\\nError\\n\"] }\n    end\n\n    context 'when there are generic warnings' do\n      let(:messages) { [warning] * 2 }\n      let(:setting) { 'report' }\n\n      it { should == [:warn, \"Warning\\nWarning\\n\"] }\n    end\n\n    context 'when there are generic errors and warnings' do\n      let(:messages) { [warning, error] * 2 }\n      let(:setting) { 'report' }\n\n      it { should == [:fail, \"Warning\\nError\\nWarning\\nError\\n\"] }\n    end\n\n    context 'when there are errors and warnings on modified/unmodified lines' do\n      let(:modified_lines) { { 'a.txt' => [2], 'b.txt' => [3, 4] } }\n      let(:setting) { 'report' }\n\n      let(:messages) do\n        [\n          warning('a.txt', 3),\n          warning('b.txt', 3),\n          error('a.txt', 2),\n          error('b.txt', 5),\n        ]\n      end\n\n      context 'and there are generic errors before them' do\n        let(:messages) { [error] * 2 + super() }\n\n        it do\n          should == [:fail, \"#{EGH}Error\\nError\\n\" \\\n                            \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n\n      context 'and there are generic warnings before them' do\n        let(:messages) { [warning] * 2 + super() }\n\n        it do\n          should == [:fail, \"#{WGH}Warning\\nWarning\\n\" \\\n                            \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n\n      context 'and there are generic errors after them' do\n        let(:messages) { super() + [error] * 2 }\n\n        it do\n          should == [:fail, \"#{EGH}Error\\nError\\n\" \\\n                            \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n\n      context 'and there are generic warnings after them' do\n        let(:messages) { super() + [warning] * 2 }\n\n        it do\n          should == [:fail, \"#{WGH}Warning\\nWarning\\n\" \\\n                            \"#{EMH}Error\\n#{WMH}Warning\\n\" \\\n                            \"#{EUH}Error\\n#{WUH}Warning\\n\"]\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/overcommit/utils_spec.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'spec_helper'\nrequire 'securerandom'\n\ndescribe Overcommit::Utils do\n  describe '.script_path' do\n    subject { described_class.script_path('some-script') }\n\n    it 'points to the libexec scripts directory' do\n      subject.should end_with File.join('libexec', 'some-script')\n    end\n  end\n\n  describe '.repo_root' do\n    let(:repo_dir) { repo }\n    subject { described_class.repo_root }\n\n    around do |example|\n      Dir.chdir(repo_dir) do\n        example.run\n      end\n    end\n\n    it 'returns the path to the repository root' do\n      # realpath is so spec passes on Mac OS X\n      subject.should == File.realpath(repo_dir)\n    end\n\n    context 'when there is no .git directory' do\n      before do\n        FileUtils.rm_rf('.git', secure: true)\n      end\n\n      it 'raises an exception' do\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidGitRepo\n      end\n    end\n  end\n\n  describe '.git_dir' do\n    let(:repo_dir) { repo }\n    subject { described_class.git_dir }\n\n    around do |example|\n      Dir.chdir(repo_dir) do\n        example.run\n      end\n    end\n\n    context 'when .git is a directory' do\n      it 'returns the path to the directory' do\n        subject.should end_with File.join(repo_dir, '.git')\n      end\n    end\n\n    context 'when .git directory is not located in the repository' do\n      let(:git_dir) { Dir.mktmpdir }\n      let(:repo_dir) { repo(git_dir: git_dir) }\n\n      it 'returns the path of the external Git directory' do\n        # realpath is so spec passes on Mac OS X\n        subject.should == File.realpath(git_dir)\n      end\n    end\n  end\n\n  describe '.strip_color_codes' do\n    subject { described_class.strip_color_codes(text) }\n\n    context 'with an empty string' do\n      let(:text) { '' }\n\n      it { should == '' }\n    end\n\n    context 'with a string with no escape sequences' do\n      let(:text) { 'A normal string' }\n\n      it { should == text }\n    end\n\n    context 'with a string with escape sequences' do\n      let(:text) { \"A \\e[31mcolored string\\e[39m\" }\n\n      it { should == 'A colored string' }\n    end\n  end\n\n  describe '.snake_case' do\n    it 'converts camel case to underscores' do\n      described_class.snake_case('HelloWorld').should == 'hello_world'\n    end\n\n    it 'leaves underscored strings as is' do\n      described_class.snake_case('hello_world').should == 'hello_world'\n    end\n\n    it 'converts namespaced class names to paths' do\n      described_class.snake_case('SomeModule::SomeOtherModule::SomeClass').\n        should == 'some_module/some_other_module/some_class'\n    end\n  end\n\n  describe '.camel_case' do\n    it 'converts underscored strings to camel case' do\n      described_class.camel_case('hello_world').should == 'HelloWorld'\n    end\n\n    it 'leaves already camel-cased strings as is' do\n      described_class.camel_case('HelloWorld').should == 'HelloWorld'\n    end\n\n    it 'converts hyphenated strings to camel case' do\n      described_class.camel_case('hello-world').should == 'HelloWorld'\n    end\n  end\n\n  describe '.supported_hook_types' do\n    subject { described_class.supported_hook_types }\n\n    # rubocop:disable Layout/LineLength\n    it { should =~ %w[commit-msg pre-commit post-checkout post-commit post-merge post-rewrite pre-push pre-rebase prepare-commit-msg] }\n    # rubocop:enable Layout/LineLength\n  end\n\n  describe '.supported_hook_type_classes' do\n    subject { described_class.supported_hook_type_classes }\n\n    # rubocop:disable Layout/LineLength\n    it { should =~ %w[CommitMsg PreCommit PostCheckout PostCommit PostMerge PostRewrite PrePush PreRebase PrepareCommitMsg] }\n    # rubocop:enable Layout/LineLength\n  end\n\n  describe '.parent_command' do\n    subject { described_class.parent_command }\n\n    before do\n      Process.stub(:ppid) { Process.pid }\n    end\n\n    it { should =~ /rspec/ }\n\n    context 'when there is no parent' do\n      before do\n        Process.stub(:ppid) { 0 }\n      end\n\n      it { should be_nil }\n    end\n  end\n\n  describe '.execute' do\n    let(:arguments) { %w[echo Hello World] }\n    subject { described_class.execute(arguments) }\n\n    it 'returns result with the output' do\n      subject.stdout.should == \"Hello World\\n\"\n    end\n\n    it 'returns result with the exit status' do\n      subject.status.should == 0\n    end\n\n    context 'when one of the arguments is a lone pipe character' do\n      let(:arguments) { %w[ps aux | grep bash] }\n\n      it 'raises an exception' do\n        expect { subject }.to raise_error Overcommit::Exceptions::InvalidCommandArgs\n      end\n    end\n\n    context 'when given an input stream' do\n      let(:arguments) { ['cat', '-'] }\n      let(:input) { 'Hello world' }\n\n      subject { described_class.execute(arguments, input: input) }\n\n      it 'passes the input to the standard input stream' do\n        subject.stdout.should == \"Hello world\\n\"\n      end\n    end\n\n    context 'when given a list of arguments to execute in chunks' do\n      let(:arguments) { ['echo'] }\n      let(:splittable_args) { %w[1 2 3] }\n\n      subject { described_class.execute(arguments, args: splittable_args) }\n\n      it 'invokes CommandSplitter.execute' do\n        Overcommit::CommandSplitter.\n          should_receive(:execute).\n          with(arguments, { args: splittable_args }).\n          and_return(double(status: 0, stdout: '', stderr: ''))\n        subject\n      end\n\n      it 'returns a result' do\n        subject.should be_success\n        subject.stdout.should == \"1 2 3\\n\"\n        subject.stderr.should == ''\n      end\n    end\n  end\n\n  describe '.execute_in_background' do\n    let(:arguments) { %w[touch some-file] }\n\n    subject { described_class.execute_in_background(arguments) }\n\n    around do |example|\n      directory do\n        example.run\n      end\n    end\n\n    it 'executes the command' do\n      wait_until(timeout: 5) { subject.exited? } # Make sure process terminated before checking\n      File.exist?('some-file').should == true\n    end\n  end\n\n  describe '.with_environment' do\n    let(:var_name) { \"OVERCOMMIT_TEST_VAR_#{SecureRandom.hex}\" }\n\n    shared_examples_for 'with_environment' do\n      it 'sets the value of the variable within the block' do\n        described_class.with_environment var_name => 'modified_value' do\n          ENV[var_name].should == 'modified_value'\n        end\n      end\n    end\n\n    context 'when setting an environment variable that was not already set' do\n      it_should_behave_like 'with_environment'\n\n      it 'deletes the value once the block has exited' do\n        described_class.with_environment var_name => 'modified_value' do\n          # Do something...\n        end\n\n        ENV[var_name].should be_nil\n      end\n    end\n\n    context 'when setting an environment variable that was already set' do\n      around do |example|\n        ENV[var_name] = 'previous_value'\n        example.run\n        ENV.delete(var_name)\n      end\n\n      it_should_behave_like 'with_environment'\n\n      it 'restores the old value once the block has exited' do\n        described_class.with_environment var_name => 'modified_value' do\n          # Do something...\n        end\n\n        ENV[var_name].should == 'previous_value'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'bundler'\nrequire 'simplecov'\nSimpleCov.start do\n  add_filter 'bin/'\n  add_filter 'libexec/'\n  add_filter 'spec/'\n  add_filter 'template-dir/'\n\n  if ENV['CI']\n    require 'simplecov-lcov'\n\n    SimpleCov::Formatter::LcovFormatter.config do |c|\n      c.report_with_single_file = true\n      c.single_report_path = 'coverage/lcov.info'\n    end\n\n    formatter SimpleCov::Formatter::LcovFormatter\n  end\nend\n\nrequire 'overcommit'\nrequire 'tempfile'\n\nhook_types =\n  Dir[File.join(Overcommit::HOOK_DIRECTORY, '*')].\n  select { |f| File.directory?(f) }.\n  reject { |f| File.basename(f) == 'shared' }.\n  sort\n\nhook_types.each do |hook_type|\n  require File.join(hook_type, 'base.rb')\n  Dir[File.join(hook_type, '**/*.rb')].\n    select { |f| File.file?(f) && File.basename(f, '.rb') != 'base' }.\n    sort.\n    each { |f| require f }\nend\n\nDir[File.dirname(__FILE__) + '/support/**/*.rb'].sort.each { |f| require f }\n\nRSpec.configure do |config|\n  config.include GitSpecHelpers\n  config.include OutputHelpers\n  config.include ShellHelpers\n\n  # Continue to enable the older `should` syntax for expectations\n  config.expect_with :rspec do |c|\n    c.syntax = [:expect, :should]\n  end\n\n  config.mock_with :rspec do |c|\n    c.syntax = [:expect, :should]\n  end\n\n  config.around(:each) do |example|\n    # Most tests don't deal with verification, so disable it by default\n    env = {}\n    unless respond_to?(:enable_verification) && enable_verification\n      env['OVERCOMMIT_NO_VERIFY'] = '1'\n    end\n\n    Overcommit::Utils.with_environment env do\n      example.run\n    end\n  end\n\n  # Much of Overcommit depends on these helpers, so they are aggressively\n  # cached. Unset them before each example so we always get fresh values.\n  config.before(:each) do\n    %w[git_dir repo_root].each do |var|\n      Overcommit::Utils.instance_variable_set(:\"@#{var}\", nil)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/support/git_spec_helpers.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'tmpdir'\n\n# Helpers for creating temporary repositories and directories for testing.\nmodule GitSpecHelpers\n  module_function\n\n  # Creates an empty git repository, allowing you to execute a block where\n  # the current working directory is set to that repository's root directory.\n  #\n  # @param options [Hash]\n  # @return [String] path of the repository\n  def repo(options = {})\n    directory('some-repo') do\n      create_cmd = %w[git init --initial-branch=master]\n      create_cmd += ['--template', options[:template_dir]] if options[:template_dir]\n      create_cmd += ['--separate-git-dir', options[:git_dir]] if options[:git_dir]\n\n      result = Overcommit::Utils.execute(create_cmd)\n      raise \"Unable to create repo: #{result.stderr}\" unless result.success?\n\n      # Need to define user info since some CI contexts don't have defaults set\n      `git config --local user.name \"Overcommit Tester\"`\n      `git config --local user.email \"overcommit@example.com\"`\n      `git config --local rerere.enabled 0` # Don't record resolutions in tests\n      `git config --local commit.gpgsign false`\n\n      yield if block_given?\n    end\n  end\n\n  # Retrieve sha1 based on git ref\n  #\n  # @param ref [String] git ref\n  # @return [String] ref's sha1\n  def get_sha1(ref)\n    `git rev-parse #{ref}`.chomp\n  end\n\n  # Creates a directory (with an optional specific name) in a temporary\n  # directory which will automatically be destroyed.\n  #\n  # @param name [String] base name of the directory\n  # @return [String] path of the directory that was created\n  def directory(name = 'some-dir', &block)\n    tmpdir = Dir.mktmpdir.tap do |path|\n      Dir.chdir(path) do\n        Dir.mkdir(name)\n        Dir.chdir(name, &block) if block_given?\n      end\n    end\n\n    File.join(tmpdir, name)\n  end\n\n  # Returns a random git object hash.\n  #\n  # @return [String]\n  def random_hash\n    Array.new(40) { rand(65..90).chr }.join\n  end\nend\n"
  },
  {
    "path": "spec/support/matchers/hook.rb",
    "content": "# frozen_string_literal: true\n\n# General spec matcher logic for checking hook status and output.\nclass HookMatcher\n  def initialize(status, args)\n    options = args.empty? ? {} : { message: args.first }\n    @expected_status = status\n    @expected_message = options[:message]\n  end\n\n  def matches?(check)\n    result = [check.run].flatten\n    if result.is_a?(Array) &&\n       (result.first.is_a?(Overcommit::Hook::Message) || result.empty?)\n      messages_match?(result)\n    else\n      actual_status, actual_message = result\n      status_matches?(actual_status) && message_matches?(actual_message)\n    end\n  end\n\n  def messages_match?(messages)\n    case @expected_status\n    when :fail\n      messages.any? { |message| message.type == :error }\n    when :warn\n      messages.any? { |message| message.type == :warning }\n    else\n      messages.empty?\n    end\n  end\n\n  def status_matches?(actual_status)\n    @expected_status.nil? || actual_status == @expected_status\n  end\n\n  def message_matches?(actual_message)\n    return true if @expected_message.nil?\n\n    if @expected_message.is_a?(Regexp)\n      actual_message =~ @expected_message\n    else\n      actual_message == @expected_message\n    end\n  end\n\n  def failure_message(actual, error_message)\n    actual_status, actual_message = [actual].flatten\n\n    if status_matches?(actual_status)\n      error_message <<\n        \" with message matching #{@expected_message.inspect},\" \\\n        \" but was #{actual_message.inspect}\"\n    end\n\n    error_message\n  end\nend\n\n# Can't use 'fail' as it is a reserved word.\nRSpec::Matchers.define :fail_hook do |*args|\n  check_matcher = HookMatcher.new(:fail, args)\n\n  match do\n    check_matcher.matches?(actual)\n  end\n\n  failure_message do\n    check_matcher.failure_message(\n      actual,\n      'expected that the hook would fail'\n    )\n  end\n\n  failure_message_when_negated do\n    'expected that the hook would not fail'\n  end\n\n  description { 'fail' }\nend\n\nRSpec::Matchers.define :pass do |*args|\n  check_matcher = HookMatcher.new(:pass, args)\n\n  match do\n    check_matcher.matches?(actual)\n  end\n\n  failure_message do\n    check_matcher.failure_message(\n      actual,\n      'expected that the check would pass'\n    )\n  end\n\n  failure_message_when_negated do\n    'expected that the check would not pass'\n  end\n\n  description { 'pass the check' }\nend\n\nRSpec::Matchers.define :warn do |*args|\n  check_matcher = HookMatcher.new(:warn, args)\n\n  match do |check|\n    check_matcher.matches?(check)\n  end\n\n  failure_message do\n    check_matcher.failure_message(\n      actual,\n      'expected that the check would report a warning'\n    )\n  end\n\n  failure_message_when_negated do\n    'expected that the check would not report a warning'\n  end\n\n  description { 'report a warning' }\nend\n"
  },
  {
    "path": "spec/support/normalize_indent.rb",
    "content": "# frozen_string_literal: true\n\n# Strips off excess leading indentation from each line so we can use Heredocs\n# for writing code without having the leading indentation count.\nmodule IndentNormalizer\n  def normalize_indent(code)\n    leading_indent = code[/^(\\s*)/, 1]\n    code.lstrip.gsub(/\\n#{leading_indent}/, \"\\n\")\n  end\nend\n\nRSpec.configure do |_config|\n  include IndentNormalizer\nend\n"
  },
  {
    "path": "spec/support/output_helpers.rb",
    "content": "# frozen_string_literal: true\n\n# Helpers for capturing output streams in tests.\nmodule OutputHelpers\n  module_function\n\n  def capture_stdout\n    original = $stdout\n    $stdout = output = StringIO.new\n\n    yield\n\n    output.string\n  ensure\n    $stdout = original\n  end\nend\n"
  },
  {
    "path": "spec/support/shell_helpers.rb",
    "content": "# frozen_string_literal: true\n\nrequire 'timeout'\nrequire 'overcommit/subprocess'\n\n# Helpers for executing shell commands in tests.\nmodule ShellHelpers\n  def shell(command)\n    Overcommit::Subprocess.spawn(command)\n  end\n\n  def symlink(source, dest)\n    Overcommit::Utils::FileUtils.symlink(source, dest)\n  end\n\n  def touch(file)\n    FileUtils.touch(file)\n  end\n\n  # Wait until the specified condition is true or the given timeout has elapsed,\n  # whichever comes first.\n  #\n  # @param options [Hash]\n  # @raise [Timeout::TimeoutError] timeout has elapsed before condition holds\n  def wait_until(options = {})\n    Timeout.timeout(options.fetch(:timeout) { 1 }) do\n      loop do\n        return if yield\n\n        sleep options.fetch(:check_interval) { 0.1 }\n      end\n    end\n  end\n\n  # Output text to file using `File#puts`, which mimics the behavior of the\n  # `echo` shell command.\n  #\n  # @param text [String] text to write\n  # @param file [String] path to target file\n  # @param options [Hash]\n  # @option options [Boolean] :append whether to append to existing file\n  def echo(text, file, options = {})\n    mode = options[:append] ? 'a' : 'w'\n    File.open(file, mode) { |f| f.puts(text) }\n  end\nend\n"
  },
  {
    "path": "template-dir/hooks/commit-msg",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/overcommit-hook",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/post-checkout",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/post-commit",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/post-merge",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/post-rewrite",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/pre-commit",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/pre-push",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/pre-rebase",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  },
  {
    "path": "template-dir/hooks/prepare-commit-msg",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n# Entrypoint for Overcommit hook integration. Installing Overcommit will result\n# in all of your git hooks being copied from this file, allowing the framework\n# to manage your hooks for you.\n\n# Prevent a Ruby stack trace from appearing when we interrupt the hook.\n# Note that this will be overridden when Overcommit is loaded, since the\n# InterruptHandler will redefine the trap at that time.\nSignal.trap('INT') do\n  puts 'Hook run interrupted'\n  exit 130\nend\n\n# Allow hooks to be disabled via environment variable so git commands can be run\n# in scripts without Overcommit running hooks\nif ENV['OVERCOMMIT_DISABLE'].to_i != 0 || ENV['OVERCOMMIT_DISABLED'].to_i != 0\n  exit\nend\n\nhook_type = File.basename($0)\nif hook_type == 'overcommit-hook'\n  puts \"Don't run `overcommit-hook` directly; it is intended to be symlinked \" \\\n       \"by each hook in a repository's .git/hooks directory.\"\n  exit 64 # EX_USAGE\nend\n\n# Check if Overcommit should invoke a Bundler context for loading gems\nFile.read('.overcommit.yml') =~ /gemfile: (?:false|['\"]?(.*)['\"]?)/\ngemfile = Regexp.last_match(1)\n\nif gemfile\n  ENV['BUNDLE_GEMFILE'] = gemfile\n  require 'bundler'\n\n  begin\n    Bundler.setup\n  rescue Bundler::BundlerError => e\n    puts \"Problem loading '#{gemfile}': #{e.message}\"\n    puts \"Try running:\\nbundle install --gemfile=#{gemfile}\" if e.is_a?(Bundler::GemNotFound)\n    exit 78 # EX_CONFIG\n  end\nend\n\nbegin\n  require 'overcommit'\nrescue LoadError\n  if gemfile\n    puts 'You have specified the `gemfile` option in your Overcommit ' \\\n         'configuration but have not added the `overcommit` gem to ' \\\n         \"#{gemfile}.\"\n  else\n    puts 'This repository contains hooks installed by Overcommit, but the ' \\\n         \"`overcommit` gem is not installed.\\n\" \\\n         'Install it with `gem install overcommit`.'\n  end\n\n  exit 64 # EX_USAGE\nend\n\nbegin\n  logger = Overcommit::Logger.new(STDOUT)\n  Overcommit::Utils.log = logger\n\n  # Ensure master hook is up-to-date\n  installer = Overcommit::Installer.new(logger)\n  if installer.run(Overcommit::Utils.repo_root, action: :update)\n    exec($0, *ARGV) # Execute the updated hook with all original arguments\n  end\n\n  config = Overcommit::ConfigurationLoader.new(logger).load_repo_config\n\n  context = Overcommit::HookContext.create(hook_type, config, ARGV, STDIN)\n  config.apply_environment!(context, ENV)\n\n  printer = Overcommit::Printer.new(config, logger, context)\n  runner = Overcommit::HookRunner.new(config, logger, context, printer)\n\n  status = runner.run\n\n  exit(status ? 0 : 65) # 65 = EX_DATAERR\nrescue Overcommit::Exceptions::ConfigurationError => e\n  puts e\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookContextLoadError => e\n  puts e\n  puts 'Are you running an old version of Overcommit?'\n  exit 69 # EX_UNAVAILABLE\nrescue Overcommit::Exceptions::HookLoadError,\n       Overcommit::Exceptions::InvalidHookDefinition => e\n  puts e.message\n  puts e.backtrace\n  exit 78 # EX_CONFIG\nrescue Overcommit::Exceptions::HookSetupFailed,\n       Overcommit::Exceptions::HookCleanupFailed => e\n  puts e.message\n  exit 74 # EX_IOERR\nrescue Overcommit::Exceptions::HookCancelled\n  puts 'You cancelled the hook run'\n  exit 130 # Ctrl-C cancel\nrescue Overcommit::Exceptions::InvalidGitRepo => e\n  puts e\n  exit 64 # EX_USAGE\nrescue Overcommit::Exceptions::ConfigurationSignatureChanged => e\n  puts e\n  puts \"For more information, see #{Overcommit::REPO_URL}#security\"\n  exit 1\nrescue Overcommit::Exceptions::InvalidHookSignature\n  exit 1\nrescue StandardError => e\n  puts e.message\n  puts e.backtrace\n  puts \"Report this bug at #{Overcommit::BUG_REPORT_URL}\"\n  exit 70 # EX_SOFTWARE\nend\n"
  }
]