overcommit-jeygeethanmedia 0.53.1.2 → 0.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/overcommit +6 -5
- data/config/default.yml +37 -5
- data/lib/overcommit/cli.rb +14 -13
- data/lib/overcommit/command_splitter.rb +2 -0
- data/lib/overcommit/configuration.rb +1 -1
- data/lib/overcommit/configuration_loader.rb +11 -8
- data/lib/overcommit/exceptions.rb +19 -16
- data/lib/overcommit/git_config.rb +1 -0
- data/lib/overcommit/git_repo.rb +14 -13
- data/lib/overcommit/hook/commit_msg/base.rb +1 -1
- data/lib/overcommit/hook/commit_msg/spell_check.rb +1 -1
- data/lib/overcommit/hook/commit_msg/text_width.rb +1 -1
- data/lib/overcommit/hook/post_checkout/base.rb +1 -0
- data/lib/overcommit/hook/post_commit/git_guilt.rb +2 -1
- data/lib/overcommit/hook/pre_commit/author_name.rb +2 -2
- data/lib/overcommit/hook/pre_commit/bundle_audit.rb +1 -1
- data/lib/overcommit/hook/pre_commit/chamber_compare.rb +5 -5
- data/lib/overcommit/hook/pre_commit/chamber_security.rb +1 -0
- data/lib/overcommit/hook/pre_commit/coffee_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/css_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/dart_analyzer.rb +22 -0
- data/lib/overcommit/hook/pre_commit/erb_lint.rb +20 -0
- data/lib/overcommit/hook/pre_commit/es_lint.rb +3 -6
- data/lib/overcommit/hook/pre_commit/fasterer.rb +1 -1
- data/lib/overcommit/hook/pre_commit/foodcritic.rb +3 -1
- data/lib/overcommit/hook/pre_commit/hlint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/html_tidy.rb +1 -1
- data/lib/overcommit/hook/pre_commit/java_checkstyle.rb +1 -1
- data/lib/overcommit/hook/pre_commit/js_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/jsl.rb +1 -1
- data/lib/overcommit/hook/pre_commit/kt_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/license_finder.rb +1 -0
- data/lib/overcommit/hook/pre_commit/line_endings.rb +3 -2
- data/lib/overcommit/hook/pre_commit/nginx_test.rb +1 -1
- data/lib/overcommit/hook/pre_commit/php_cs.rb +1 -1
- data/lib/overcommit/hook/pre_commit/php_cs_fixer.rb +1 -1
- data/lib/overcommit/hook/pre_commit/php_lint.rb +3 -3
- data/lib/overcommit/hook/pre_commit/php_stan.rb +1 -1
- data/lib/overcommit/hook/pre_commit/puppet_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/pyflakes.rb +1 -1
- data/lib/overcommit/hook/pre_commit/pylint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/python_flake8.rb +1 -1
- data/lib/overcommit/hook/pre_commit/rails_best_practices.rb +1 -1
- data/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb +2 -2
- data/lib/overcommit/hook/pre_commit/rst_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/scalariform.rb +1 -1
- data/lib/overcommit/hook/pre_commit/scalastyle.rb +1 -1
- data/lib/overcommit/hook/pre_commit/scss_lint.rb +3 -3
- data/lib/overcommit/hook/pre_commit/semi_standard.rb +1 -1
- data/lib/overcommit/hook/pre_commit/sqlint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/standard.rb +1 -1
- data/lib/overcommit/hook/pre_commit/stylelint.rb +2 -1
- data/lib/overcommit/hook/pre_commit/swift_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/ts_lint.rb +4 -4
- data/lib/overcommit/hook/pre_commit/w3c_css.rb +4 -4
- data/lib/overcommit/hook/pre_commit/w3c_html.rb +4 -4
- data/lib/overcommit/hook/pre_commit/xml_lint.rb +1 -1
- data/lib/overcommit/hook/pre_commit/yaml_lint.rb +25 -4
- data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +7 -3
- data/lib/overcommit/hook/pre_commit/yard_coverage.rb +0 -1
- data/lib/overcommit/hook/pre_push/cargo_test.rb +1 -0
- data/lib/overcommit/hook/pre_push/flutter_test.rb +16 -0
- data/lib/overcommit/hook/pre_push/pub_test.rb +16 -0
- data/lib/overcommit/hook/prepare_commit_msg/base.rb +1 -0
- data/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb +38 -4
- data/lib/overcommit/hook/shared/bower_install.rb +1 -0
- data/lib/overcommit/hook/shared/bundle_install.rb +1 -0
- data/lib/overcommit/hook/shared/composer_install.rb +1 -0
- data/lib/overcommit/hook/shared/npm_install.rb +1 -0
- data/lib/overcommit/hook/shared/pronto.rb +7 -3
- data/lib/overcommit/hook/shared/yarn_install.rb +1 -0
- data/lib/overcommit/hook_context/commit_msg.rb +7 -0
- data/lib/overcommit/hook_context/helpers/file_modifications.rb +79 -0
- data/lib/overcommit/hook_context/helpers/stash_unstaged_changes.rb +144 -0
- data/lib/overcommit/hook_context/post_commit.rb +1 -0
- data/lib/overcommit/hook_context/pre_commit.rb +5 -199
- data/lib/overcommit/hook_context/pre_push.rb +1 -0
- data/lib/overcommit/hook_context/run_all.rb +1 -0
- data/lib/overcommit/hook_context.rb +3 -3
- data/lib/overcommit/hook_loader/base.rb +3 -3
- data/lib/overcommit/hook_loader/plugin_hook_loader.rb +3 -3
- data/lib/overcommit/hook_runner.rb +8 -7
- data/lib/overcommit/hook_signer.rb +1 -0
- data/lib/overcommit/installer.rb +2 -1
- data/lib/overcommit/subprocess.rb +24 -2
- data/lib/overcommit/utils/messages_utils.rb +1 -0
- data/lib/overcommit/version.rb +1 -1
- data/libexec/index-tags +6 -4
- data/template-dir/hooks/commit-msg +27 -20
- data/template-dir/hooks/overcommit-hook +27 -20
- data/template-dir/hooks/post-checkout +27 -20
- data/template-dir/hooks/post-commit +27 -20
- data/template-dir/hooks/post-merge +27 -20
- data/template-dir/hooks/post-rewrite +27 -20
- data/template-dir/hooks/pre-commit +27 -20
- data/template-dir/hooks/pre-push +27 -20
- data/template-dir/hooks/pre-rebase +27 -20
- data/template-dir/hooks/prepare-commit-msg +27 -20
- metadata +27 -7
@@ -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,79 @@
|
|
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
|
+
|
15
|
+
amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'
|
16
|
+
|
17
|
+
# Since the ps command can return invalid byte sequences for commands
|
18
|
+
# containing unicode characters, we replace the offending characters,
|
19
|
+
# since the pattern we're looking for will consist of ASCII characters
|
20
|
+
unless cmd.valid_encoding?
|
21
|
+
cmd = Overcommit::Utils.
|
22
|
+
parent_command.
|
23
|
+
encode('UTF-16be', invalid: :replace, replace: '?').
|
24
|
+
encode('UTF-8')
|
25
|
+
end
|
26
|
+
|
27
|
+
return @amendment if
|
28
|
+
# True if the command is a commit with the --amend flag
|
29
|
+
@amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?
|
30
|
+
|
31
|
+
# Check for git aliases that call `commit --amend`
|
32
|
+
`git config --get-regexp "^alias\\." "#{amend_pattern}"`.
|
33
|
+
scan(/alias\.([-\w]+)/). # Extract the alias
|
34
|
+
each do |match|
|
35
|
+
return @amendment if
|
36
|
+
# True if the command uses a git alias for `commit --amend`
|
37
|
+
@amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
@amendment
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a list of added, copied, or modified files that have been staged.
|
44
|
+
# Renames and deletions are ignored, since there should be nothing to check.
|
45
|
+
def modified_files
|
46
|
+
unless @modified_files
|
47
|
+
currently_staged = Overcommit::GitRepo.modified_files(staged: true)
|
48
|
+
@modified_files = currently_staged
|
49
|
+
|
50
|
+
# Include files modified in last commit if amending
|
51
|
+
if amendment?
|
52
|
+
subcmd = 'show --format=%n'
|
53
|
+
previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
|
54
|
+
@modified_files |= filter_modified_files(previously_modified)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@modified_files
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the set of line numbers corresponding to the lines that were
|
61
|
+
# changed in a specified file.
|
62
|
+
def modified_lines_in_file(file)
|
63
|
+
@modified_lines ||= {}
|
64
|
+
unless @modified_lines[file]
|
65
|
+
@modified_lines[file] =
|
66
|
+
Overcommit::GitRepo.extract_modified_lines(file, staged: true)
|
67
|
+
|
68
|
+
# Include lines modified in last commit if amending
|
69
|
+
if amendment?
|
70
|
+
subcmd = 'show --format=%n'
|
71
|
+
@modified_lines[file] +=
|
72
|
+
Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@modified_lines[file]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,144 @@
|
|
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
|
+
|
30
|
+
@initial_commit = Overcommit::GitRepo.initial_commit?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Restore unstaged changes and reset file modification times so it appears
|
34
|
+
# as if nothing ever changed.
|
35
|
+
#
|
36
|
+
# We want to restore the modification times for each of the files after
|
37
|
+
# every step to ensure as little time as possible has passed while the
|
38
|
+
# modification time on the file was newer. This helps us play more nicely
|
39
|
+
# with file watchers.
|
40
|
+
def cleanup_environment
|
41
|
+
if @changes_stashed
|
42
|
+
clear_working_tree
|
43
|
+
restore_working_tree
|
44
|
+
restore_modified_times
|
45
|
+
end
|
46
|
+
|
47
|
+
Overcommit::GitRepo.restore_merge_state
|
48
|
+
Overcommit::GitRepo.restore_cherry_pick_state
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Stores the modification times for all modified files to make it appear like
|
54
|
+
# they never changed.
|
55
|
+
#
|
56
|
+
# This prevents (some) editors from complaining about files changing when we
|
57
|
+
# stash changes before running the hooks.
|
58
|
+
def store_modified_times
|
59
|
+
@modified_times = {}
|
60
|
+
|
61
|
+
staged_files = modified_files
|
62
|
+
unstaged_files = Overcommit::GitRepo.modified_files(staged: false)
|
63
|
+
|
64
|
+
(staged_files + unstaged_files).each do |file|
|
65
|
+
next if Overcommit::Utils.broken_symlink?(file)
|
66
|
+
next unless File.exist?(file) # Ignore renamed files (old file no longer exists)
|
67
|
+
|
68
|
+
@modified_times[file] = File.mtime(file)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns whether there are any changes to tracked files which have not yet
|
73
|
+
# been staged.
|
74
|
+
def unstaged_changes?
|
75
|
+
result = Overcommit::Utils.execute(%w[git --no-pager diff --quiet])
|
76
|
+
!result.success?
|
77
|
+
end
|
78
|
+
|
79
|
+
def stash_changes
|
80
|
+
@stash_attempted = true
|
81
|
+
|
82
|
+
stash_message = "Overcommit: Stash of repo state before hook run at #{Time.now}"
|
83
|
+
result = Overcommit::Utils.with_environment('GIT_LITERAL_PATHSPECS' => '0') do
|
84
|
+
Overcommit::Utils.execute(
|
85
|
+
%w[git -c commit.gpgsign=false stash save --keep-index --quiet] + [stash_message]
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
unless result.success?
|
90
|
+
# Failure to stash in this case is likely due to a configuration
|
91
|
+
# issue (e.g. author/email not set or GPG signing key incorrect)
|
92
|
+
raise Overcommit::Exceptions::HookSetupFailed,
|
93
|
+
"Unable to setup environment for #{hook_script_name} hook run:" \
|
94
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
95
|
+
end
|
96
|
+
|
97
|
+
@changes_stashed = `git stash list -1`.include?(stash_message)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Restores the file modification times for all modified files to make it
|
101
|
+
# appear like they never changed.
|
102
|
+
def restore_modified_times
|
103
|
+
@modified_times.each do |file, time|
|
104
|
+
next if Overcommit::Utils.broken_symlink?(file)
|
105
|
+
next unless File.exist?(file)
|
106
|
+
|
107
|
+
File.utime(time, time, file)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Clears the working tree so that the stash can be applied.
|
112
|
+
def clear_working_tree
|
113
|
+
removed_submodules = Overcommit::GitRepo.staged_submodule_removals
|
114
|
+
|
115
|
+
result = Overcommit::Utils.execute(%w[git reset --hard])
|
116
|
+
unless result.success?
|
117
|
+
raise Overcommit::Exceptions::HookCleanupFailed,
|
118
|
+
"Unable to cleanup working tree after #{hook_script_name} hooks run:" \
|
119
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
120
|
+
end
|
121
|
+
|
122
|
+
# Hard-resetting a staged submodule removal results in the index being
|
123
|
+
# reset but the submodule being restored as an empty directory. This empty
|
124
|
+
# directory prevents us from stashing on a subsequent run if a hook fails.
|
125
|
+
#
|
126
|
+
# Work around this by removing these empty submodule directories as there
|
127
|
+
# doesn't appear any reason to keep them around.
|
128
|
+
removed_submodules.each do |submodule|
|
129
|
+
FileUtils.rmdir(submodule.path)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Applies the stash to the working tree to restore the user's state.
|
134
|
+
def restore_working_tree
|
135
|
+
result = Overcommit::Utils.execute(%w[git stash pop --index])
|
136
|
+
unless result.success?
|
137
|
+
raise Overcommit::Exceptions::HookCleanupFailed,
|
138
|
+
"Unable to restore working tree after #{hook_script_name} hooks run:" \
|
139
|
+
"\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
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
|
@@ -76,6 +76,7 @@ module Overcommit::HookContext
|
|
76
76
|
|
77
77
|
def overwritten_commits
|
78
78
|
return @overwritten_commits if defined? @overwritten_commits
|
79
|
+
|
79
80
|
result = Overcommit::Subprocess.spawn(%W[git rev-list #{remote_sha1} ^#{local_sha1}])
|
80
81
|
if result.success?
|
81
82
|
result.stdout.split("\n")
|
@@ -9,11 +9,11 @@ module Overcommit::HookContext
|
|
9
9
|
require "overcommit/hook_context/#{underscored_hook_type}"
|
10
10
|
|
11
11
|
Overcommit::HookContext.const_get(hook_type_class).new(config, args, input)
|
12
|
-
rescue LoadError, NameError =>
|
12
|
+
rescue LoadError, NameError => e
|
13
13
|
# Could happen when a symlink was created for a hook type Overcommit does
|
14
14
|
# not yet support.
|
15
15
|
raise Overcommit::Exceptions::HookContextLoadError,
|
16
|
-
"Unable to load '#{hook_type}' hook context: '#{
|
17
|
-
|
16
|
+
"Unable to load '#{hook_type}' hook context: '#{e}'",
|
17
|
+
e.backtrace
|
18
18
|
end
|
19
19
|
end
|
@@ -38,10 +38,10 @@ module Overcommit::HookLoader
|
|
38
38
|
Overcommit::Hook.const_get(@context.hook_class_name).
|
39
39
|
const_get(hook_name).
|
40
40
|
new(@config, @context)
|
41
|
-
rescue LoadError, NameError =>
|
41
|
+
rescue LoadError, NameError => e
|
42
42
|
raise Overcommit::Exceptions::HookLoadError,
|
43
|
-
"Unable to load hook '#{hook_name}': #{
|
44
|
-
|
43
|
+
"Unable to load hook '#{hook_name}': #{e}",
|
44
|
+
e.backtrace
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -93,10 +93,10 @@ module Overcommit::HookLoader
|
|
93
93
|
end
|
94
94
|
|
95
95
|
hook_module.const_set(hook_name, hook_class).new(@config, @context)
|
96
|
-
rescue LoadError, NameError =>
|
96
|
+
rescue LoadError, NameError => e
|
97
97
|
raise Overcommit::Exceptions::HookLoadError,
|
98
|
-
"Unable to load hook '#{hook_name}': #{
|
99
|
-
|
98
|
+
"Unable to load hook '#{hook_name}': #{e}",
|
99
|
+
e.backtrace
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -94,6 +94,7 @@ module Overcommit
|
|
94
94
|
loop do
|
95
95
|
hook = @lock.synchronize { @hooks_left.pop }
|
96
96
|
break unless hook
|
97
|
+
|
97
98
|
run_hook(hook)
|
98
99
|
end
|
99
100
|
end
|
@@ -159,12 +160,12 @@ module Overcommit
|
|
159
160
|
return if should_skip?(hook)
|
160
161
|
|
161
162
|
status, output = hook.run_and_transform
|
162
|
-
rescue Overcommit::Exceptions::MessageProcessingError =>
|
163
|
+
rescue Overcommit::Exceptions::MessageProcessingError => e
|
163
164
|
status = :fail
|
164
|
-
output =
|
165
|
-
rescue StandardError =>
|
165
|
+
output = e.message
|
166
|
+
rescue StandardError => e
|
166
167
|
status = :fail
|
167
|
-
output = "Hook raised unexpected error\n#{
|
168
|
+
output = "Hook raised unexpected error\n#{e.message}\n#{e.backtrace.join("\n")}"
|
168
169
|
end
|
169
170
|
|
170
171
|
@failed = true if status == :fail
|
@@ -202,7 +203,7 @@ module Overcommit
|
|
202
203
|
|
203
204
|
# Load plugin hooks after so they can subclass existing hooks
|
204
205
|
@hooks += HookLoader::PluginHookLoader.new(@config, @context, @log).load_hooks
|
205
|
-
rescue LoadError =>
|
206
|
+
rescue LoadError => e
|
206
207
|
# Include a more helpful message that will probably save some confusion
|
207
208
|
message = 'A load error occurred. ' +
|
208
209
|
if @config['gemfile']
|
@@ -212,8 +213,8 @@ module Overcommit
|
|
212
213
|
end
|
213
214
|
|
214
215
|
raise Overcommit::Exceptions::HookLoadError,
|
215
|
-
"#{message}\n#{
|
216
|
-
|
216
|
+
"#{message}\n#{e.message}",
|
217
|
+
e.backtrace
|
217
218
|
end
|
218
219
|
end
|
219
220
|
end
|
data/lib/overcommit/installer.rb
CHANGED
@@ -4,7 +4,7 @@ require 'fileutils'
|
|
4
4
|
|
5
5
|
module Overcommit
|
6
6
|
# Manages the installation of Overcommit hooks in a git repository.
|
7
|
-
class Installer # rubocop:disable ClassLength
|
7
|
+
class Installer # rubocop:disable Metrics/ClassLength
|
8
8
|
TEMPLATE_DIRECTORY = File.join(Overcommit::HOME, 'template-dir')
|
9
9
|
MASTER_HOOK = File.join(TEMPLATE_DIRECTORY, 'hooks', 'overcommit-hook')
|
10
10
|
|
@@ -174,6 +174,7 @@ module Overcommit
|
|
174
174
|
repo_config_file = File.join(@target, Overcommit::CONFIG_FILE_NAME)
|
175
175
|
|
176
176
|
return if File.exist?(repo_config_file)
|
177
|
+
|
177
178
|
FileUtils.cp(File.join(Overcommit::HOME, 'config', 'starter.yml'), repo_config_file)
|
178
179
|
end
|
179
180
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'childprocess'
|
4
4
|
require 'tempfile'
|
5
|
+
require 'overcommit/os'
|
5
6
|
|
6
7
|
module Overcommit
|
7
8
|
# Manages execution of a child process, collecting the exit status and
|
@@ -38,7 +39,7 @@ module Overcommit
|
|
38
39
|
if options[:input]
|
39
40
|
begin
|
40
41
|
process.io.stdin.puts(options[:input])
|
41
|
-
rescue StandardError
|
42
|
+
rescue StandardError
|
42
43
|
# Silently ignore if the standard input stream of the spawned
|
43
44
|
# process is closed before we get a chance to write to it. This
|
44
45
|
# happens on JRuby a lot.
|
@@ -51,7 +52,7 @@ module Overcommit
|
|
51
52
|
err.rewind
|
52
53
|
out.rewind
|
53
54
|
|
54
|
-
Result.new(process.exit_code, out.read, err.read)
|
55
|
+
Result.new(process.exit_code, to_utf8(out.read), to_utf8(err.read))
|
55
56
|
end
|
56
57
|
|
57
58
|
# Spawns a new process in the background using the given array of
|
@@ -83,6 +84,27 @@ module Overcommit
|
|
83
84
|
%w[cmd.exe /c] + [args.join(' ')]
|
84
85
|
end
|
85
86
|
|
87
|
+
# Convert string from current locale to utf-8
|
88
|
+
#
|
89
|
+
# When running commands under windows the command output is using
|
90
|
+
# current system locale (depends on system lanuage) not UTF-8
|
91
|
+
#
|
92
|
+
# @param process [String]
|
93
|
+
# @return [String]
|
94
|
+
def to_utf8(string)
|
95
|
+
# Our encoding code doesn't work on the GitHub Actions Windows
|
96
|
+
# environment for unknown reasons, so just skip it in CI.
|
97
|
+
return string if OS.windows? && ENV['GITHUB_ACTIONS']
|
98
|
+
|
99
|
+
if Encoding.locale_charmap == 'UTF-8'
|
100
|
+
return string
|
101
|
+
end
|
102
|
+
|
103
|
+
ec = Encoding::Converter.new(Encoding.locale_charmap, 'UTF-8')
|
104
|
+
# Convert encoding, alternatively simple: string.scrub will suffice
|
105
|
+
ec.convert(string)
|
106
|
+
end
|
107
|
+
|
86
108
|
# @param process [ChildProcess]
|
87
109
|
# @return [Array<IO>]
|
88
110
|
def assign_output_streams(process)
|
data/lib/overcommit/version.rb
CHANGED