overcommit 0.52.1 → 0.56.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +16 -4
- data/lib/overcommit/exceptions.rb +19 -16
- data/lib/overcommit/git_repo.rb +10 -9
- data/lib/overcommit/hook/base.rb +2 -1
- data/lib/overcommit/hook/pre_commit/author_name.rb +2 -2
- data/lib/overcommit/hook/pre_commit/php_cs.rb +6 -13
- data/lib/overcommit/hook/pre_commit/pronto.rb +3 -14
- data/lib/overcommit/hook/pre_push/base.rb +5 -0
- data/lib/overcommit/hook/pre_push/pronto.rb +12 -0
- data/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb +38 -4
- data/lib/overcommit/hook/shared/pronto.rb +21 -0
- data/lib/overcommit/hook_context/commit_msg.rb +7 -0
- data/lib/overcommit/hook_context/helpers/file_modifications.rb +78 -0
- data/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb +141 -0
- data/lib/overcommit/hook_context/pre_commit.rb +5 -199
- data/lib/overcommit/printer.rb +1 -0
- data/lib/overcommit/version.rb +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6ecbb7e4b390f8d17c9058da38464b73f7cec50a87e494430e9c15a9c40ecff
|
4
|
+
data.tar.gz: f90974df2fe0b918ff8711a228608ad1b312a730abd9ad7c45b02ee512daa496
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8b55f73d42ca1791bbd6a01281a0adbbe1f57385721d9ec4905e98dd9217480ae5b10ff8e9083048953974d40c97d104f126ca9b514fb05359ba6c402b4bf7b
|
7
|
+
data.tar.gz: b9263d9c65fbbeaa4cf7a38d5b7c2bb4af6d6ec7abf5e3ee22209a455a3b7529718b6eb9adbc14161539861420242093c7b4c3564e248ce84bc663cfb354899f
|
data/config/default.yml
CHANGED
@@ -338,7 +338,7 @@ PreCommit:
|
|
338
338
|
keywords: ['FContext','FDescribe','FIt','FMeasure','FSpecify','FWhen']
|
339
339
|
|
340
340
|
GoFmt:
|
341
|
-
enabled:
|
341
|
+
enabled: false
|
342
342
|
description: 'Fix with go fmt'
|
343
343
|
required_executable: 'go'
|
344
344
|
command: ['go', 'fmt']
|
@@ -885,7 +885,7 @@ PreCommit:
|
|
885
885
|
enabled: false
|
886
886
|
description: 'Analyze with YAMLlint'
|
887
887
|
required_executable: 'yamllint'
|
888
|
-
flags: ['--format=parsable']
|
888
|
+
flags: ['--format=parsable', '--strict']
|
889
889
|
install_command: 'pip install yamllint'
|
890
890
|
include:
|
891
891
|
- '**/*.yaml'
|
@@ -1237,9 +1237,14 @@ PrepareCommitMsg:
|
|
1237
1237
|
ReplaceBranch:
|
1238
1238
|
enabled: false
|
1239
1239
|
description: 'Prepends the commit message with text based on the branch name'
|
1240
|
-
branch_pattern: '\A
|
1240
|
+
branch_pattern: '\A(\d+)-(\w+).*\z'
|
1241
1241
|
replacement_text: '[#\1]'
|
1242
|
-
skipped_commit_types:
|
1242
|
+
skipped_commit_types:
|
1243
|
+
- 'message' # if message is given via `-m`, `-F`
|
1244
|
+
- 'template' # if `-t` is given or `commit.template` is set
|
1245
|
+
- 'commit' # if `-c`, `-C`, or `--amend` is given
|
1246
|
+
- 'merge' # if merging
|
1247
|
+
- 'squash' # if squashing
|
1243
1248
|
on_fail: warn
|
1244
1249
|
|
1245
1250
|
# Hooks that run during `git push`, after remote refs have been updated but
|
@@ -1297,6 +1302,13 @@ PrePush:
|
|
1297
1302
|
flags: ['--bootstrap', 'vendor/autoload.php', 'tests']
|
1298
1303
|
install_command: 'composer require --dev phpunit/phpunit'
|
1299
1304
|
|
1305
|
+
Pronto:
|
1306
|
+
enabled: false
|
1307
|
+
description: 'Analyzing with pronto'
|
1308
|
+
required_executable: 'pronto'
|
1309
|
+
install_command: 'gem install pronto'
|
1310
|
+
flags: ['run', '--exit-code']
|
1311
|
+
|
1300
1312
|
ProtectedBranches:
|
1301
1313
|
enabled: false
|
1302
1314
|
description: 'Check for illegal pushes to protected branches'
|
@@ -1,52 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Overcommit::Exceptions
|
4
|
+
# Base error class.
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
4
7
|
# Raised when a {Configuration} could not be loaded from a file.
|
5
|
-
class ConfigurationError <
|
8
|
+
class ConfigurationError < Error; end
|
6
9
|
|
7
10
|
# Raised when the Overcommit configuration file signature has changed.
|
8
|
-
class ConfigurationSignatureChanged <
|
11
|
+
class ConfigurationSignatureChanged < Error; end
|
9
12
|
|
10
13
|
# Raised when trying to read/write to/from the local repo git config fails.
|
11
|
-
class GitConfigError <
|
14
|
+
class GitConfigError < Error; end
|
12
15
|
|
13
16
|
# Raised when there was a problem reading submodule information for a repo.
|
14
|
-
class GitSubmoduleError <
|
17
|
+
class GitSubmoduleError < Error; end
|
15
18
|
|
16
19
|
# Raised when there was a problem reading git revision information with `rev-list`.
|
17
|
-
class GitRevListError <
|
20
|
+
class GitRevListError < Error; end
|
18
21
|
|
19
22
|
# Raised when a {HookContext} is unable to setup the environment before a run.
|
20
|
-
class HookSetupFailed <
|
23
|
+
class HookSetupFailed < Error; end
|
21
24
|
|
22
25
|
# Raised when a {HookContext} is unable to clean the environment after a run.
|
23
|
-
class HookCleanupFailed <
|
26
|
+
class HookCleanupFailed < Error; end
|
24
27
|
|
25
28
|
# Raised when a hook run was cancelled by the user.
|
26
|
-
class HookCancelled <
|
29
|
+
class HookCancelled < Error; end
|
27
30
|
|
28
31
|
# Raised when a hook could not be loaded by a {HookRunner}.
|
29
|
-
class HookLoadError <
|
32
|
+
class HookLoadError < Error; end
|
30
33
|
|
31
34
|
# Raised when a {HookRunner} could not be loaded.
|
32
|
-
class HookContextLoadError <
|
35
|
+
class HookContextLoadError < Error; end
|
33
36
|
|
34
37
|
# Raised when a pipe character is used in the `execute` helper, as this was
|
35
38
|
# likely used in error.
|
36
|
-
class InvalidCommandArgs <
|
39
|
+
class InvalidCommandArgs < Error; end
|
37
40
|
|
38
41
|
# Raised when an installation target is not a valid git repository.
|
39
|
-
class InvalidGitRepo <
|
42
|
+
class InvalidGitRepo < Error; end
|
40
43
|
|
41
44
|
# Raised when a hook was defined incorrectly.
|
42
|
-
class InvalidHookDefinition <
|
45
|
+
class InvalidHookDefinition < Error; end
|
43
46
|
|
44
47
|
# Raised when one or more hook plugin signatures have changed.
|
45
|
-
class InvalidHookSignature <
|
48
|
+
class InvalidHookSignature < Error; end
|
46
49
|
|
47
50
|
# Raised when there is a problem processing output into {Hook::Messages}s.
|
48
|
-
class MessageProcessingError <
|
51
|
+
class MessageProcessingError < Error; end
|
49
52
|
|
50
53
|
# Raised when an installation target already contains non-Overcommit hooks.
|
51
|
-
class PreExistingHooks <
|
54
|
+
class PreExistingHooks < Error; end
|
52
55
|
end
|
data/lib/overcommit/git_repo.rb
CHANGED
@@ -109,15 +109,16 @@ module Overcommit
|
|
109
109
|
# @return [Array<String>] list of absolute file paths
|
110
110
|
def list_files(paths = [], options = {})
|
111
111
|
ref = options[:ref] || 'HEAD'
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
112
|
+
|
113
|
+
result = Overcommit::Utils.execute(%W[git ls-tree --name-only #{ref}], args: paths)
|
114
|
+
unless result.success?
|
115
|
+
raise Overcommit::Exceptions::Error,
|
116
|
+
"Error listing files. EXIT STATUS(es): #{result.statuses}.\n" \
|
117
|
+
"STDOUT(s): #{result.stdouts}.\n" \
|
118
|
+
"STDERR(s): #{result.stderrs}."
|
119
|
+
end
|
120
|
+
|
121
|
+
result.stdout.split(/\n/).
|
121
122
|
map { |relative_file| File.expand_path(relative_file) }.
|
122
123
|
reject { |file| File.directory?(file) } # Exclude submodule directories
|
123
124
|
end
|
data/lib/overcommit/hook/base.rb
CHANGED
@@ -12,9 +12,9 @@ module Overcommit::Hook::PreCommit
|
|
12
12
|
result.stdout.chomp
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
if name.empty?
|
16
16
|
return :fail,
|
17
|
-
"Author must
|
17
|
+
"Author name must be non-0 in length.\n" \
|
18
18
|
'Set your name with `git config --global user.name "Your Name"` ' \
|
19
19
|
'or via the GIT_AUTHOR_NAME environment variable'
|
20
20
|
end
|
@@ -12,21 +12,14 @@ module Overcommit::Hook::PreCommit
|
|
12
12
|
def run
|
13
13
|
messages = []
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# Discard the csv header
|
21
|
-
rows.shift
|
22
|
-
|
23
|
-
# Push each of the errors in the particular file into the array
|
24
|
-
rows.map do |row|
|
25
|
-
messages << row
|
26
|
-
end
|
27
|
-
end
|
15
|
+
result = execute(command, args: applicable_files)
|
16
|
+
if result.status
|
17
|
+
messages = result.stdout.split("\n")
|
18
|
+
# Discard the csv header
|
19
|
+
messages.shift
|
28
20
|
end
|
29
21
|
|
22
|
+
return :fail if messages.empty? && !result.success?
|
30
23
|
return :pass if messages.empty?
|
31
24
|
|
32
25
|
parse_messages(messages)
|
@@ -1,23 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'overcommit/hook/shared/pronto'
|
4
|
+
|
3
5
|
module Overcommit::Hook::PreCommit
|
4
6
|
# Runs `pronto`
|
5
7
|
#
|
6
8
|
# @see https://github.com/mmozuras/pronto
|
7
9
|
class Pronto < Base
|
8
|
-
|
9
|
-
type.include?('E') ? :error : :warning
|
10
|
-
end
|
11
|
-
|
12
|
-
def run
|
13
|
-
result = execute(command)
|
14
|
-
return :pass if result.success?
|
15
|
-
|
16
|
-
extract_messages(
|
17
|
-
result.stdout.split("\n"),
|
18
|
-
/^(?<file>(?:\w:)?[^:]+):(?<line>\d+) (?<type>[^ ]+)/,
|
19
|
-
MESSAGE_TYPE_CATEGORIZER,
|
20
|
-
)
|
21
|
-
end
|
10
|
+
include Overcommit::Hook::Shared::Pronto
|
22
11
|
end
|
23
12
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
require 'overcommit/utils/messages_utils'
|
4
5
|
|
5
6
|
module Overcommit::Hook::PrePush
|
6
7
|
# Functionality common to all pre-push hooks.
|
@@ -17,6 +18,10 @@ module Overcommit::Hook::PrePush
|
|
17
18
|
|
18
19
|
private
|
19
20
|
|
21
|
+
def extract_messages(*args)
|
22
|
+
Overcommit::Utils::MessagesUtils.extract_messages(*args)
|
23
|
+
end
|
24
|
+
|
20
25
|
def exclude_remotes
|
21
26
|
@config['exclude_remotes'] || []
|
22
27
|
end
|
@@ -2,11 +2,37 @@
|
|
2
2
|
|
3
3
|
module Overcommit::Hook::PrepareCommitMsg
|
4
4
|
# Prepends the commit message with a message based on the branch name.
|
5
|
+
#
|
6
|
+
# === What to prepend
|
7
|
+
#
|
5
8
|
# It's possible to reference parts of the branch name through the captures in
|
6
9
|
# the `branch_pattern` regex.
|
10
|
+
#
|
11
|
+
# For instance, if your current branch is `123-topic` then this config
|
12
|
+
#
|
13
|
+
# branch_pattern: '(\d+)-(\w+)'
|
14
|
+
# replacement_text: '[#\1]'
|
15
|
+
#
|
16
|
+
# would make this hook prepend commit messages with `[#123]`.
|
17
|
+
#
|
18
|
+
# Similarly, a replacement text of `[\1][\2]` would result in `[123][topic]`.
|
19
|
+
#
|
20
|
+
# == When to run this hook
|
21
|
+
#
|
22
|
+
# You can configure this to run only for specific types of commits by setting
|
23
|
+
# the `skipped_commit_types`. The allowed types are
|
24
|
+
#
|
25
|
+
# - 'message' - if message is given via `-m`, `-F`
|
26
|
+
# - 'template' - if `-t` is given or `commit.template` is set
|
27
|
+
# - 'commit' - if `-c`, `-C`, or `--amend` is given
|
28
|
+
# - 'merge' - if merging
|
29
|
+
# - 'squash' - if squashing
|
30
|
+
#
|
7
31
|
class ReplaceBranch < Base
|
32
|
+
DEFAULT_BRANCH_PATTERN = /\A(\d+)-(\w+).*\z/
|
33
|
+
|
8
34
|
def run
|
9
|
-
return :pass if
|
35
|
+
return :pass if skip?
|
10
36
|
|
11
37
|
Overcommit::Utils.log.debug(
|
12
38
|
"Checking if '#{Overcommit::GitRepo.current_branch}' matches #{branch_pattern}"
|
@@ -17,21 +43,25 @@ module Overcommit::Hook::PrepareCommitMsg
|
|
17
43
|
Overcommit::Utils.log.debug("Writing #{commit_message_filename} with #{new_template}")
|
18
44
|
|
19
45
|
modify_commit_message do |old_contents|
|
20
|
-
"#{new_template}
|
46
|
+
"#{new_template}#{old_contents}"
|
21
47
|
end
|
22
48
|
|
23
49
|
:pass
|
24
50
|
end
|
25
51
|
|
26
52
|
def new_template
|
27
|
-
@new_template ||=
|
53
|
+
@new_template ||=
|
54
|
+
begin
|
55
|
+
curr_branch = Overcommit::GitRepo.current_branch
|
56
|
+
curr_branch.gsub(branch_pattern, replacement_text).strip
|
57
|
+
end
|
28
58
|
end
|
29
59
|
|
30
60
|
def branch_pattern
|
31
61
|
@branch_pattern ||=
|
32
62
|
begin
|
33
63
|
pattern = config['branch_pattern']
|
34
|
-
Regexp.new((pattern || '').empty? ?
|
64
|
+
Regexp.new((pattern || '').empty? ? DEFAULT_BRANCH_PATTERN : pattern)
|
35
65
|
end
|
36
66
|
end
|
37
67
|
|
@@ -53,5 +83,9 @@ module Overcommit::Hook::PrepareCommitMsg
|
|
53
83
|
def skipped_commit_types
|
54
84
|
@skipped_commit_types ||= config['skipped_commit_types'].map(&:to_sym)
|
55
85
|
end
|
86
|
+
|
87
|
+
def skip?
|
88
|
+
skipped_commit_types.include?(commit_message_source)
|
89
|
+
end
|
56
90
|
end
|
57
91
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Overcommit::Hook::Shared
|
4
|
+
# Shared code used by all Pronto hooks. Runs pronto linter.
|
5
|
+
module Pronto
|
6
|
+
MESSAGE_TYPE_CATEGORIZER = lambda do |type|
|
7
|
+
type.include?('E') ? :error : :warning
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
result = execute(command)
|
12
|
+
return :pass if result.success?
|
13
|
+
|
14
|
+
extract_messages(
|
15
|
+
result.stdout.split("\n"),
|
16
|
+
/^(?<file>(?:\w:)?[^:]+):(?<line>\d+) (?<type>[^ ]+)/,
|
17
|
+
MESSAGE_TYPE_CATEGORIZER,
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,8 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'pre_commit'
|
4
|
+
require_relative 'helpers/stash_unstaged_changes'
|
5
|
+
require_relative 'helpers/file_modifications'
|
6
|
+
|
3
7
|
module Overcommit::HookContext
|
4
8
|
# Contains helpers related to contextual information used by commit-msg hooks.
|
5
9
|
class CommitMsg < Base
|
10
|
+
include Overcommit::HookContext::Helpers::StashUnstagedChanges
|
11
|
+
include Overcommit::HookContext::Helpers::FileModifications
|
12
|
+
|
6
13
|
def empty_message?
|
7
14
|
commit_message.strip.empty?
|
8
15
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Overcommit::HookContext
|
4
|
+
module Helpers
|
5
|
+
# This module contains methods for determining what files were changed and on what unique line
|
6
|
+
# numbers did the change occur.
|
7
|
+
module FileModifications
|
8
|
+
# Returns whether this hook run was triggered by `git commit --amend`
|
9
|
+
def amendment?
|
10
|
+
return @amendment unless @amendment.nil?
|
11
|
+
|
12
|
+
cmd = Overcommit::Utils.parent_command
|
13
|
+
return unless cmd
|
14
|
+
amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'
|
15
|
+
|
16
|
+
# Since the ps command can return invalid byte sequences for commands
|
17
|
+
# containing unicode characters, we replace the offending characters,
|
18
|
+
# since the pattern we're looking for will consist of ASCII characters
|
19
|
+
unless cmd.valid_encoding?
|
20
|
+
cmd = Overcommit::Utils.
|
21
|
+
parent_command.
|
22
|
+
encode('UTF-16be', invalid: :replace, replace: '?').
|
23
|
+
encode('UTF-8')
|
24
|
+
end
|
25
|
+
|
26
|
+
return @amendment if
|
27
|
+
# True if the command is a commit with the --amend flag
|
28
|
+
@amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?
|
29
|
+
|
30
|
+
# Check for git aliases that call `commit --amend`
|
31
|
+
`git config --get-regexp "^alias\\." "#{amend_pattern}"`.
|
32
|
+
scan(/alias\.([-\w]+)/). # Extract the alias
|
33
|
+
each do |match|
|
34
|
+
return @amendment if
|
35
|
+
# True if the command uses a git alias for `commit --amend`
|
36
|
+
@amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
@amendment
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get a list of added, copied, or modified files that have been staged.
|
43
|
+
# Renames and deletions are ignored, since there should be nothing to check.
|
44
|
+
def modified_files
|
45
|
+
unless @modified_files
|
46
|
+
currently_staged = Overcommit::GitRepo.modified_files(staged: true)
|
47
|
+
@modified_files = currently_staged
|
48
|
+
|
49
|
+
# Include files modified in last commit if amending
|
50
|
+
if amendment?
|
51
|
+
subcmd = 'show --format=%n'
|
52
|
+
previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
|
53
|
+
@modified_files |= filter_modified_files(previously_modified)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@modified_files
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the set of line numbers corresponding to the lines that were
|
60
|
+
# changed in a specified file.
|
61
|
+
def modified_lines_in_file(file)
|
62
|
+
@modified_lines ||= {}
|
63
|
+
unless @modified_lines[file]
|
64
|
+
@modified_lines[file] =
|
65
|
+
Overcommit::GitRepo.extract_modified_lines(file, staged: true)
|
66
|
+
|
67
|
+
# Include lines modified in last commit if amending
|
68
|
+
if amendment?
|
69
|
+
subcmd = 'show --format=%n'
|
70
|
+
@modified_lines[file] +=
|
71
|
+
Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@modified_lines[file]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Overcommit::HookContext
|
4
|
+
module Helpers
|
5
|
+
# This module contains behavior for stashing unstaged changes before hooks are ran and restoring
|
6
|
+
# them afterwards
|
7
|
+
module StashUnstagedChanges
|
8
|
+
# Stash unstaged contents of files so hooks don't see changes that aren't
|
9
|
+
# about to be committed.
|
10
|
+
def setup_environment
|
11
|
+
store_modified_times
|
12
|
+
Overcommit::GitRepo.store_merge_state
|
13
|
+
Overcommit::GitRepo.store_cherry_pick_state
|
14
|
+
|
15
|
+
# Don't attempt to stash changes if all changes are staged, as this
|
16
|
+
# prevents us from modifying files at all, which plays better with
|
17
|
+
# editors/tools which watch for file changes.
|
18
|
+
if !initial_commit? && unstaged_changes?
|
19
|
+
stash_changes
|
20
|
+
|
21
|
+
# While running hooks make it appear as if nothing changed
|
22
|
+
restore_modified_times
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns whether the current git branch is empty (has no commits).
|
27
|
+
def initial_commit?
|
28
|
+
return @initial_commit unless @initial_commit.nil?
|
29
|
+
@initial_commit = Overcommit::GitRepo.initial_commit?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Restore unstaged changes and reset file modification times so it appears
|
33
|
+
# as if nothing ever changed.
|
34
|
+
#
|
35
|
+
# We want to restore the modification times for each of the files after
|
36
|
+
# every step to ensure as little time as possible has passed while the
|
37
|
+
# modification time on the file was newer. This helps us play more nicely
|
38
|
+
# with file watchers.
|
39
|
+
def cleanup_environment
|
40
|
+
if @changes_stashed
|
41
|
+
clear_working_tree
|
42
|
+
restore_working_tree
|
43
|
+
restore_modified_times
|
44
|
+
end
|
45
|
+
|
46
|
+
Overcommit::GitRepo.restore_merge_state
|
47
|
+
Overcommit::GitRepo.restore_cherry_pick_state
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Stores the modification times for all modified files to make it appear like
|
53
|
+
# they never changed.
|
54
|
+
#
|
55
|
+
# This prevents (some) editors from complaining about files changing when we
|
56
|
+
# stash changes before running the hooks.
|
57
|
+
def store_modified_times
|
58
|
+
@modified_times = {}
|
59
|
+
|
60
|
+
staged_files = modified_files
|
61
|
+
unstaged_files = Overcommit::GitRepo.modified_files(staged: false)
|
62
|
+
|
63
|
+
(staged_files + unstaged_files).each do |file|
|
64
|
+
next if Overcommit::Utils.broken_symlink?(file)
|
65
|
+
next unless File.exist?(file) # Ignore renamed files (old file no longer exists)
|
66
|
+
@modified_times[file] = File.mtime(file)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns whether there are any changes to tracked files which have not yet
|
71
|
+
# been staged.
|
72
|
+
def unstaged_changes?
|
73
|
+
result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])
|
74
|
+
!result.success?
|
75
|
+
end
|
76
|
+
|
77
|
+
def stash_changes
|
78
|
+
@stash_attempted = true
|
79
|
+
|
80
|
+
stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}"
|
81
|
+
result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do
|
82
|
+
Overcommit::Utils.execute(
|
83
|
+
%w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
unless result.success?
|
88
|
+
# Failure to stash in this case is likely due to a configuration
|
89
|
+
# issue (e.g. author/email not set or GPG signing key incorrect)
|
90
|
+
raise Overcommit::Exceptions::HookSetupFailed,
|
91
|
+
"Unable to setup environment for #{hook_script_name} hook run:" \
|
92
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
93
|
+
end
|
94
|
+
|
95
|
+
@changes_stashed = `git stash list -1`.include?(stash_message)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Restores the file modification times for all modified files to make it
|
99
|
+
# appear like they never changed.
|
100
|
+
def restore_modified_times
|
101
|
+
@modified_times.each do |file, time|
|
102
|
+
next if Overcommit::Utils.broken_symlink?(file)
|
103
|
+
next unless File.exist?(file)
|
104
|
+
File.utime(time, time, file)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Clears the working tree so that the stash can be applied.
|
109
|
+
def clear_working_tree
|
110
|
+
removed_submodules = Overcommit::GitRepo.staged_submodule_removals
|
111
|
+
|
112
|
+
result = Overcommit::Utils.execute(%w[git reset --hard])
|
113
|
+
unless result.success?
|
114
|
+
raise Overcommit::Exceptions::HookCleanupFailed,
|
115
|
+
"Unable to cleanup working tree after #{hook_script_name} hooks run:" \
|
116
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Hard-resetting a staged submodule removal results in the index being
|
120
|
+
# reset but the submodule being restored as an empty directory. This empty
|
121
|
+
# directory prevents us from stashing on a subsequent run if a hook fails.
|
122
|
+
#
|
123
|
+
# Work around this by removing these empty submodule directories as there
|
124
|
+
# doesn't appear any reason to keep them around.
|
125
|
+
removed_submodules.each do |submodule|
|
126
|
+
FileUtils.rmdir(submodule.path)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Applies the stash to the working tree to restore the user's state.
|
131
|
+
def restore_working_tree
|
132
|
+
result = Overcommit::Utils.execute(%w[git stash pop --index --quiet])
|
133
|
+
unless result.success?
|
134
|
+
raise Overcommit::Exceptions::HookCleanupFailed,
|
135
|
+
"Unable to restore working tree after #{hook_script_name} hooks run:" \
|
136
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'set'
|
5
|
+
require_relative 'helpers/stash_unstaged_changes'
|
6
|
+
require_relative 'helpers/file_modifications'
|
5
7
|
|
6
8
|
module Overcommit::HookContext
|
7
9
|
# Contains helpers related to contextual information used by pre-commit hooks.
|
@@ -9,204 +11,8 @@ module Overcommit::HookContext
|
|
9
11
|
# This includes staged files, which lines of those files have been modified,
|
10
12
|
# etc. It is also responsible for saving/restoring the state of the repo so
|
11
13
|
# hooks only inspect staged changes.
|
12
|
-
class PreCommit < Base
|
13
|
-
|
14
|
-
|
15
|
-
return @amendment unless @amendment.nil?
|
16
|
-
|
17
|
-
cmd = Overcommit::Utils.parent_command
|
18
|
-
return unless cmd
|
19
|
-
amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'
|
20
|
-
|
21
|
-
# Since the ps command can return invalid byte sequences for commands
|
22
|
-
# containing unicode characters, we replace the offending characters,
|
23
|
-
# since the pattern we're looking for will consist of ASCII characters
|
24
|
-
unless cmd.valid_encoding?
|
25
|
-
cmd = Overcommit::Utils.parent_command.encode('UTF-16be', invalid: :replace, replace: '?').
|
26
|
-
encode('UTF-8')
|
27
|
-
end
|
28
|
-
|
29
|
-
return @amendment if
|
30
|
-
# True if the command is a commit with the --amend flag
|
31
|
-
@amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?
|
32
|
-
|
33
|
-
# Check for git aliases that call `commit --amend`
|
34
|
-
`git config --get-regexp "^alias\\." "#{amend_pattern}"`.
|
35
|
-
scan(/alias\.([-\w]+)/). # Extract the alias
|
36
|
-
each do |match|
|
37
|
-
return @amendment if
|
38
|
-
# True if the command uses a git alias for `commit --amend`
|
39
|
-
@amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
|
40
|
-
end
|
41
|
-
|
42
|
-
@amendment
|
43
|
-
end
|
44
|
-
|
45
|
-
# Stash unstaged contents of files so hooks don't see changes that aren't
|
46
|
-
# about to be committed.
|
47
|
-
def setup_environment
|
48
|
-
store_modified_times
|
49
|
-
Overcommit::GitRepo.store_merge_state
|
50
|
-
Overcommit::GitRepo.store_cherry_pick_state
|
51
|
-
|
52
|
-
# Don't attempt to stash changes if all changes are staged, as this
|
53
|
-
# prevents us from modifying files at all, which plays better with
|
54
|
-
# editors/tools which watch for file changes.
|
55
|
-
if !initial_commit? && unstaged_changes?
|
56
|
-
stash_changes
|
57
|
-
|
58
|
-
# While running hooks make it appear as if nothing changed
|
59
|
-
restore_modified_times
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Restore unstaged changes and reset file modification times so it appears
|
64
|
-
# as if nothing ever changed.
|
65
|
-
#
|
66
|
-
# We want to restore the modification times for each of the files after
|
67
|
-
# every step to ensure as little time as possible has passed while the
|
68
|
-
# modification time on the file was newer. This helps us play more nicely
|
69
|
-
# with file watchers.
|
70
|
-
def cleanup_environment
|
71
|
-
if @changes_stashed
|
72
|
-
clear_working_tree
|
73
|
-
restore_working_tree
|
74
|
-
restore_modified_times
|
75
|
-
end
|
76
|
-
|
77
|
-
Overcommit::GitRepo.restore_merge_state
|
78
|
-
Overcommit::GitRepo.restore_cherry_pick_state
|
79
|
-
end
|
80
|
-
|
81
|
-
# Get a list of added, copied, or modified files that have been staged.
|
82
|
-
# Renames and deletions are ignored, since there should be nothing to check.
|
83
|
-
def modified_files
|
84
|
-
unless @modified_files
|
85
|
-
currently_staged = Overcommit::GitRepo.modified_files(staged: true)
|
86
|
-
@modified_files = currently_staged
|
87
|
-
|
88
|
-
# Include files modified in last commit if amending
|
89
|
-
if amendment?
|
90
|
-
subcmd = 'show --format=%n'
|
91
|
-
previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
|
92
|
-
@modified_files |= filter_modified_files(previously_modified)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
@modified_files
|
96
|
-
end
|
97
|
-
|
98
|
-
# Returns the set of line numbers corresponding to the lines that were
|
99
|
-
# changed in a specified file.
|
100
|
-
def modified_lines_in_file(file)
|
101
|
-
@modified_lines ||= {}
|
102
|
-
unless @modified_lines[file]
|
103
|
-
@modified_lines[file] =
|
104
|
-
Overcommit::GitRepo.extract_modified_lines(file, staged: true)
|
105
|
-
|
106
|
-
# Include lines modified in last commit if amending
|
107
|
-
if amendment?
|
108
|
-
subcmd = 'show --format=%n'
|
109
|
-
@modified_lines[file] +=
|
110
|
-
Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
@modified_lines[file]
|
114
|
-
end
|
115
|
-
|
116
|
-
# Returns whether the current git branch is empty (has no commits).
|
117
|
-
def initial_commit?
|
118
|
-
return @initial_commit unless @initial_commit.nil?
|
119
|
-
@initial_commit = Overcommit::GitRepo.initial_commit?
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def stash_changes
|
125
|
-
@stash_attempted = true
|
126
|
-
|
127
|
-
stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}"
|
128
|
-
result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do
|
129
|
-
Overcommit::Utils.execute(
|
130
|
-
%w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]
|
131
|
-
)
|
132
|
-
end
|
133
|
-
|
134
|
-
unless result.success?
|
135
|
-
# Failure to stash in this case is likely due to a configuration
|
136
|
-
# issue (e.g. author/email not set or GPG signing key incorrect)
|
137
|
-
raise Overcommit::Exceptions::HookSetupFailed,
|
138
|
-
"Unable to setup environment for #{hook_script_name} hook run:" \
|
139
|
-
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
140
|
-
end
|
141
|
-
|
142
|
-
@changes_stashed = `git stash list -1`.include?(stash_message)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Clears the working tree so that the stash can be applied.
|
146
|
-
def clear_working_tree
|
147
|
-
removed_submodules = Overcommit::GitRepo.staged_submodule_removals
|
148
|
-
|
149
|
-
result = Overcommit::Utils.execute(%w[git reset --hard])
|
150
|
-
unless result.success?
|
151
|
-
raise Overcommit::Exceptions::HookCleanupFailed,
|
152
|
-
"Unable to cleanup working tree after #{hook_script_name} hooks run:" \
|
153
|
-
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
154
|
-
end
|
155
|
-
|
156
|
-
# Hard-resetting a staged submodule removal results in the index being
|
157
|
-
# reset but the submodule being restored as an empty directory. This empty
|
158
|
-
# directory prevents us from stashing on a subsequent run if a hook fails.
|
159
|
-
#
|
160
|
-
# Work around this by removing these empty submodule directories as there
|
161
|
-
# doesn't appear any reason to keep them around.
|
162
|
-
removed_submodules.each do |submodule|
|
163
|
-
FileUtils.rmdir(submodule.path)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Applies the stash to the working tree to restore the user's state.
|
168
|
-
def restore_working_tree
|
169
|
-
result = Overcommit::Utils.execute(%w[git stash pop --index --quiet])
|
170
|
-
unless result.success?
|
171
|
-
raise Overcommit::Exceptions::HookCleanupFailed,
|
172
|
-
"Unable to restore working tree after #{hook_script_name} hooks run:" \
|
173
|
-
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# Returns whether there are any changes to tracked files which have not yet
|
178
|
-
# been staged.
|
179
|
-
def unstaged_changes?
|
180
|
-
result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])
|
181
|
-
!result.success?
|
182
|
-
end
|
183
|
-
|
184
|
-
# Stores the modification times for all modified files to make it appear like
|
185
|
-
# they never changed.
|
186
|
-
#
|
187
|
-
# This prevents (some) editors from complaining about files changing when we
|
188
|
-
# stash changes before running the hooks.
|
189
|
-
def store_modified_times
|
190
|
-
@modified_times = {}
|
191
|
-
|
192
|
-
staged_files = modified_files
|
193
|
-
unstaged_files = Overcommit::GitRepo.modified_files(staged: false)
|
194
|
-
|
195
|
-
(staged_files + unstaged_files).each do |file|
|
196
|
-
next if Overcommit::Utils.broken_symlink?(file)
|
197
|
-
next unless File.exist?(file) # Ignore renamed files (old file no longer exists)
|
198
|
-
@modified_times[file] = File.mtime(file)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# Restores the file modification times for all modified files to make it
|
203
|
-
# appear like they never changed.
|
204
|
-
def restore_modified_times
|
205
|
-
@modified_times.each do |file, time|
|
206
|
-
next if Overcommit::Utils.broken_symlink?(file)
|
207
|
-
next unless File.exist?(file)
|
208
|
-
File.utime(time, time, file)
|
209
|
-
end
|
210
|
-
end
|
14
|
+
class PreCommit < Base
|
15
|
+
include Overcommit::HookContext::Helpers::StashUnstagedChanges
|
16
|
+
include Overcommit::HookContext::Helpers::FileModifications
|
211
17
|
end
|
212
18
|
end
|
data/lib/overcommit/printer.rb
CHANGED
data/lib/overcommit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: overcommit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.56.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane da Silva
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: childprocess
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: 0.6.3
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '5'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: 0.6.3
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '5'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: iniparse
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -216,6 +216,7 @@ files:
|
|
216
216
|
- lib/overcommit/hook/pre_push/golangci_lint.rb
|
217
217
|
- lib/overcommit/hook/pre_push/minitest.rb
|
218
218
|
- lib/overcommit/hook/pre_push/php_unit.rb
|
219
|
+
- lib/overcommit/hook/pre_push/pronto.rb
|
219
220
|
- lib/overcommit/hook/pre_push/protected_branches.rb
|
220
221
|
- lib/overcommit/hook/pre_push/pytest.rb
|
221
222
|
- lib/overcommit/hook/pre_push/python_nose.rb
|
@@ -231,12 +232,15 @@ files:
|
|
231
232
|
- lib/overcommit/hook/shared/composer_install.rb
|
232
233
|
- lib/overcommit/hook/shared/index_tags.rb
|
233
234
|
- lib/overcommit/hook/shared/npm_install.rb
|
235
|
+
- lib/overcommit/hook/shared/pronto.rb
|
234
236
|
- lib/overcommit/hook/shared/rake_target.rb
|
235
237
|
- lib/overcommit/hook/shared/submodule_status.rb
|
236
238
|
- lib/overcommit/hook/shared/yarn_install.rb
|
237
239
|
- lib/overcommit/hook_context.rb
|
238
240
|
- lib/overcommit/hook_context/base.rb
|
239
241
|
- lib/overcommit/hook_context/commit_msg.rb
|
242
|
+
- lib/overcommit/hook_context/helpers/file_modifications.rb
|
243
|
+
- lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb
|
240
244
|
- lib/overcommit/hook_context/post_checkout.rb
|
241
245
|
- lib/overcommit/hook_context/post_commit.rb
|
242
246
|
- lib/overcommit/hook_context/post_merge.rb
|