rabbitt-githooks 1.2.7 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,9 @@
1
- class Process::Status
2
- def failed?
3
- !success?
1
+ module Process
2
+ class Status
3
+ def failed?
4
+ !success?
5
+ end
6
+ alias_method :fail?, :failed?
7
+ alias_method :failure?, :failed?
4
8
  end
5
- alias_method :fail?, :failed?
6
- alias_method :failure?, :failed?
7
9
  end
data/lib/githooks/hook.rb CHANGED
@@ -63,19 +63,24 @@ module GitHooks
63
63
  end
64
64
 
65
65
  attr_reader :sections, :phase, :repository, :repository_path
66
- attr_accessor :args, :unstaged, :untracked
66
+ attr_accessor :args, :staged, :untracked, :tracked
67
67
 
68
68
  def initialize(phase)
69
69
  @phase = phase
70
- @sections = []
70
+ @sections = {}
71
71
  @commands = []
72
72
  @args = []
73
- @unstaged = false
73
+ @staged = true
74
+ @tracked = false
74
75
  @untracked = false
75
76
 
76
77
  repository_path = Dir.getwd # rubocop:disable UselessAssignment
77
78
  end
78
79
 
80
+ def [](name)
81
+ @sections[name]
82
+ end
83
+
79
84
  def repository_path=(path)
80
85
  @repository = Repository.new(path)
81
86
  end
@@ -86,7 +91,7 @@ module GitHooks
86
91
 
87
92
  def run
88
93
  # only run sections that have actions matching files in the manifest
89
- runable_sections = @sections.select { |section| !section.actions.empty? }
94
+ runable_sections = sections.select { |section| !section.actions.empty? }
90
95
  runable_sections.collect { |section| section.run }.all?
91
96
  end
92
97
 
@@ -111,6 +116,10 @@ module GitHooks
111
116
  @commands.select { |command| command.aliases.include? name.to_s }.first
112
117
  end
113
118
 
119
+ def sections
120
+ @sections.values
121
+ end
122
+
114
123
  # DSL methods
115
124
 
116
125
  def command(name, options = {})
@@ -123,7 +132,14 @@ module GitHooks
123
132
  end
124
133
 
125
134
  def section(name, &block)
126
- @sections << Section.new(name, self, &block)
135
+ key_name = Section.key_from_name(name)
136
+ return @sections[key_name] unless block_given?
137
+
138
+ if @sections.include? key_name
139
+ @sections[key_name].instance_eval(&block)
140
+ else
141
+ @sections[key_name] ||= Section.new(name, self, &block)
142
+ end
127
143
  self
128
144
  end
129
145
 
@@ -141,8 +157,9 @@ module GitHooks
141
157
 
142
158
  def manifest
143
159
  @files ||= repo.manifest(
144
- untracked: hook.untracked,
145
- unstaged: hook.unstaged
160
+ staged: hook.staged,
161
+ tracked: hook.tracked,
162
+ untracked: hook.untracked
146
163
  )
147
164
  end
148
165
 
@@ -22,7 +22,7 @@ require 'singleton'
22
22
  require 'open3'
23
23
 
24
24
  module GitHooks
25
- class Repository
25
+ class Repository # rubocop:disable ClassLength
26
26
  autoload :Config, 'githooks/repository/config'
27
27
  autoload :File, 'githooks/repository/file'
28
28
  autoload :Limiter, 'githooks/repository/limiter'
@@ -34,12 +34,12 @@ module GitHooks
34
34
  renamed: 'R', retyped: 'T',
35
35
  unknown: 'U', unmerged: 'X',
36
36
  broken: 'B', untracked: '?',
37
- any: '*'
37
+ any: '*', tracked: '^'
38
38
  }.freeze unless defined? CHANGE_TYPE_SYMBOLS
39
39
 
40
40
  CHANGE_TYPES = CHANGE_TYPE_SYMBOLS.invert.freeze unless defined? CHANGE_TYPES
41
41
 
42
- DEFAULT_DIFF_INDEX_OPTIONS = { staged: true, ref: 'HEAD' } unless defined? DEFAULT_DIFF_INDEX_OPTIONS
42
+ DEFAULT_DIFF_INDEX_OPTIONS = { staged: true } unless defined? DEFAULT_DIFF_INDEX_OPTIONS
43
43
 
44
44
  @__instance__ = {}
45
45
  @__mutex__ = Mutex.new
@@ -91,28 +91,39 @@ module GitHooks
91
91
  end
92
92
 
93
93
  def manifest(options = {})
94
- ref = options.delete(:ref) || 'HEAD'
95
- unstaged = options.delete(:unstaged)
96
- untracked = options.delete(:untracked)
94
+ ref = options.delete(:ref)
97
95
 
98
- return staged_manifest(ref: ref) unless unstaged || untracked
96
+ return staged_manifest(ref: ref) if options.delete(:staged)
99
97
 
100
- [].tap do |files|
101
- files.push(*unstaged_manifest(ref: ref)) if unstaged
102
- files.push(*untracked_manifest) if untracked
103
- end
98
+ files = unstaged_manifest(ref: ref)
99
+
100
+ tracked_manifest(ref: ref).each do |file|
101
+ files << file unless files.index { |f| f.path.to_s == file.path.to_s }
102
+ end if options.delete(:tracked)
103
+
104
+ untracked_manifest(ref: ref).each do |file|
105
+ files << file unless files.index { |f| f.path.to_s == file.path.to_s }
106
+ end if options.delete(:untracked)
107
+
108
+ files.sort_by! { |f| f.path.to_s }
109
+ files.uniq { |f| f.path.to_s }
104
110
  end
105
111
 
106
112
  def staged_manifest(options = {})
107
- diff_index(options.merge(unstaged: false))
113
+ diff_index(options.merge(staged: true))
108
114
  end
109
115
  alias_method :commit_manifest, :staged_manifest
110
116
 
111
117
  def unstaged_manifest(options = {})
112
- diff_index(options.merge(unstaged: true))
118
+ diff_index(options.merge(staged: false))
113
119
  end
114
120
 
115
- def untracked_manifest
121
+ def tracked_manifest(*)
122
+ files = git_command('ls-files', '--exclude-standard').output.strip.split(/\s*\n\s*/)
123
+ files.collect { |path| DiffIndexEntry.from_file_path(path, true).to_repo_file }
124
+ end
125
+
126
+ def untracked_manifest(*)
116
127
  files = git_command('ls-files', '--others', '--exclude-standard').output.strip.split(/\s*\n\s*/)
117
128
  files.collect { |path| DiffIndexEntry.from_file_path(path).to_repo_file }
118
129
  end
@@ -122,11 +133,17 @@ module GitHooks
122
133
  def diff_index(options = {})
123
134
  options = DEFAULT_DIFF_INDEX_OPTIONS.merge(options)
124
135
 
125
- cmd = %w(diff-index -C -M -B)
126
- cmd << '--cached' unless options[:unstaged]
127
- cmd << options.delete(:ref) || 'HEAD'
136
+ if $stdout.tty? && !options[:staged]
137
+ cmd = %w(diff-files -C -M -B)
138
+ else
139
+ cmd = %w(diff-index -C -M -B)
140
+ cmd << '--cached' if options[:staged]
141
+ cmd << (options.delete(:ref) || 'HEAD')
142
+ end
143
+
144
+ cmd.compact!
128
145
 
129
- raw_output = git_command(*cmd).output.strip
146
+ raw_output = git_command(*cmd.compact).output.strip
130
147
  raw_output.split(/\n/).collect { |data| DiffIndexEntry.new(data).to_repo_file }
131
148
  end
132
149
 
@@ -22,149 +22,156 @@ require 'singleton'
22
22
  require 'open3'
23
23
 
24
24
  module GitHooks
25
- class Repository::Config # rubocop:disable ClassLength
26
- OPTIONS = {
27
- 'path' => { type: :path, multiple: false },
28
- 'script' => { type: :path, multiple: false },
29
- 'pre-run-execute' => { type: :path, multiple: true },
30
- 'post-run-execute' => { type: :path, multiple: true }
31
- }
32
-
33
- def initialize(path = Dir.getwd)
34
- @repository = Repository.instance(path)
35
- end
36
-
37
- OPTIONS.keys.each do |name|
38
- method_name = name.gsub(/-/, '_')
39
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
40
- def #{method_name}(options = {})
41
- result = get('#{name}', options)
42
- OPTIONS['#{name}'][:multiple] ? [result].flatten.compact : result
43
- end
44
- EOS
45
- end
25
+ class Repository
26
+ class Config # rubocop:disable ClassLength
27
+ OPTIONS = {
28
+ 'path' => { type: :path, multiple: false },
29
+ 'script' => { type: :path, multiple: false },
30
+ 'pre-run-execute' => { type: :path, multiple: true },
31
+ 'post-run-execute' => { type: :path, multiple: true }
32
+ }
33
+
34
+ def initialize(path = Dir.getwd)
35
+ @repository = Repository.instance(path)
36
+ end
46
37
 
47
- def [](option)
48
- send(option.gsub('-', '_'))
49
- end
38
+ OPTIONS.keys.each do |name|
39
+ method_name = name.gsub(/-/, '_')
40
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
41
+ def #{method_name}(options = {})
42
+ result = get('#{name}', options)
43
+ OPTIONS['#{name}'][:multiple] ? [result].flatten.compact : result
44
+ end
45
+ EOS
46
+ end
50
47
 
51
- def set(option, value, options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
52
- unless OPTIONS.keys.include? option
53
- fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
48
+ def [](option)
49
+ send(option.gsub('-', '_'))
54
50
  end
55
51
 
56
- repo = options.delete(:repo_path) || repo_path
57
- global = (opt = options.delete(:global)).nil? ? false : opt
58
- var_type = "--#{OPTIONS[option][:type]}"
59
- add_type = OPTIONS[option][:multiple] ? '--add' : '--replace-all'
60
- overwrite = !!options.delete(:overwrite)
61
-
62
- if option == 'path'
63
- new_path = Pathname.new(value)
64
- errors = []
65
- errors << 'path must be a real location' unless new_path.exist?
66
- errors << 'path must be a directory' unless new_path.directory?
67
- errors << 'path must have a hooks directory in it' unless (new_path + 'hooks').exist?
68
-
69
- if errors.size > 0
70
- puts "Unable to change githooks path for [#{repo}]:"
71
- errors.each { |error| puts " #{error}" }
72
- fail ArgumentError
52
+ def set(option, value, options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
53
+ unless OPTIONS.keys.include? option
54
+ fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
55
+ end
56
+
57
+ repo = options.delete(:repo_path) || repo_path
58
+ global = (opt = options.delete(:global)).nil? ? false : opt
59
+ var_type = "--#{OPTIONS[option][:type]}"
60
+ add_type = OPTIONS[option][:multiple] ? '--add' : '--replace-all'
61
+ overwrite = !!options.delete(:overwrite)
62
+
63
+ if option == 'path'
64
+ new_path = Pathname.new(value)
65
+ errors = []
66
+ errors << 'path must be a real location' unless new_path.exist?
67
+ errors << 'path must be a directory' unless new_path.directory?
68
+ unless (new_path + 'hooks').exist? || (new_path + '.hooks').exist?
69
+ errors << 'path must have a hooks or .hooks directory in it'
70
+ end
71
+
72
+ if errors.size > 0
73
+ puts "Unable to change githooks path for [#{repo}]:"
74
+ errors.each { |error| puts " #{error}" }
75
+ fail ArgumentError
76
+ end
77
+ else
78
+ fail ArgumentError unless Pathname.new(value).executable?
73
79
  end
74
- else
75
- fail ArgumentError unless Pathname.new(value).executable?
76
- end
77
80
 
78
- value = Pathname.new(value).realpath.to_s
81
+ value = Pathname.new(value).realpath.to_s
79
82
 
80
- if overwrite && !self[option].nil? && !self[option].empty?
81
- puts "Overwrite requested for option '#{option}'" if GitHooks.verbose
82
- unset(option, repo_path: repo, global: global)
83
- end
83
+ if overwrite && !self[option].nil? && !self[option].empty?
84
+ puts "Overwrite requested for option '#{option}'" if GitHooks.verbose
85
+ unset(option, repo_path: repo, global: global)
86
+ end
84
87
 
85
- option = "githooks.#{repo}.#{option}"
86
- git_command(global ? '--global' : '--local', var_type, add_type, option, value, path: repo).tap do |result|
87
- puts "Added option #{option} with value #{value}" if result.status.success?
88
+ option = "githooks.#{repo}.#{option}"
89
+ git_command(global ? '--global' : '--local', var_type, add_type, option, value, path: repo).tap do |result|
90
+ puts "Added option #{option} with value #{value}" if result.status.success?
91
+ end
88
92
  end
89
- end
90
93
 
91
- def remove_section(options = {})
92
- repo = options.delete(:repo_path) || repo_path
93
- global = (opt = options.delete(:global)).nil? ? false : opt
94
- option = "githooks.#{repo}"
95
- git_command(global ? '--global' : '--local', '--remove-section', option, path: repo)
96
- end
97
-
98
- def unset(option, *args)
99
- unless OPTIONS.keys.include? option
100
- fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
94
+ def remove_section(options = {})
95
+ repo = options.delete(:repo_path) || repo_path
96
+ global = (opt = options.delete(:global)).nil? ? false : opt
97
+ option = "githooks.#{repo}"
98
+ git_command(global ? '--global' : '--local', '--remove-section', option, path: repo)
101
99
  end
102
100
 
103
- options = args.extract_options
104
- repo = options.delete(:repo_path) || repo_path
105
- global = (opt = options.delete(:global)).nil? ? false : opt
106
- option = "githooks.#{repo}.#{option}"
101
+ def unset(option, *args) # rubocop:disable Style/CyclomaticComplexity
102
+ unless OPTIONS.keys.include? option
103
+ fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
104
+ end
107
105
 
108
- value_regex = args.first
106
+ options = args.extract_options
107
+ repo = options.delete(:repo_path) || repo_path
108
+ global = (opt = options.delete(:global)).nil? ? false : opt
109
+ option = "githooks.#{repo}.#{option}"
109
110
 
110
- if options.delete(:all) || value_regex.nil?
111
- git_command(global ? '--global' : '--local', '--unset-all', option, path: repo)
112
- else
113
- git_command(global ? '--global' : '--local', '--unset', option, value_regex, path: repo)
114
- end.tap do |result|
115
- puts "Unset option #{option.git_option_path_split.last}" if result.status.success?
116
- end
117
- end
111
+ value_regex = args.first
118
112
 
119
- def get(option, options = {})
120
- unless OPTIONS.keys.include? option
121
- fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
113
+ if options.delete(:all) || value_regex.nil?
114
+ git_command(global ? '--global' : '--local', '--unset-all', option, path: repo)
115
+ else
116
+ git_command(global ? '--global' : '--local', '--unset', option, value_regex, path: repo)
117
+ end.tap do |result|
118
+ puts "Unset option #{option.git_option_path_split.last}" if result.status.success?
119
+ end
122
120
  end
123
121
 
124
- repo = options[:repo_path] || repo_path
125
- githooks = list(options)['githooks']
122
+ def get(option, options = {})
123
+ unless OPTIONS.keys.include? option
124
+ fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
125
+ end
126
126
 
127
- githooks[repo][option] if githooks && githooks[repo] && githooks[repo][option]
128
- end
127
+ repo = options[:repo_path] || repo_path
128
+ githooks = list(options)['githooks']
129
129
 
130
- def list(options = {}) # rubocop:disable MethodLength, CyclomaticComplexity
131
- repo = options.delete(:repo_path) || repo_path
132
- global = (opt = options.delete(:global)).nil? ? false : opt
130
+ githooks[repo][option] if githooks && githooks[repo] && githooks[repo][option]
131
+ end
133
132
 
134
- config_list = git_command('--list', global ? '--global' : '--local', path: repo).output.split(/\n/)
135
- config_list.inject({}) do |hash, line|
136
- key, value = line.split(/\s*=\s*/)
137
- key_parts = key.git_option_path_split
133
+ def list(options = {})
134
+ config(options.delete(:repo_path) || repo_path)
135
+ end
138
136
 
139
- ptr = hash[key_parts.shift] ||= {} # rubocop:disable IndentationWidth
140
- while key_parts.size > 1 && (part = key_parts.shift)
141
- ptr = ptr[part] ||= {} # rubocop:disable IndentationWidth
142
- end
137
+ private
143
138
 
144
- key = key_parts.shift
145
- case ptr[key]
146
- when nil then ptr[key] = value
147
- when Array then ptr[key] << value
148
- else ptr[key] = [ptr[key], value].flatten
149
- end
139
+ def repo_path
140
+ @repository.root_path
141
+ end
150
142
 
151
- hash
143
+ def git_command(*args)
144
+ args = ['config', *args].flatten
145
+ @repository.git_command(*args)
152
146
  end
153
- end
154
147
 
155
- private
148
+ def config(path = nil) # rubocop:disable MethodLength, CyclomaticComplexity
149
+ path ||= repo_path
156
150
 
157
- def repo_path
158
- @repository.root_path
159
- end
151
+ raw_config = git_command('--list', path: path).output.split("\n")
152
+ raw_config.sort.uniq.inject({}) do |hash, line|
153
+ key, value = line.split(/\s*=\s*/)
154
+ key_parts = key.git_option_path_split
160
155
 
161
- def git_command(*args)
162
- args = ['config', *args].flatten
163
- @repository.git_command(*args)
164
- end
156
+ ptr = hash[key_parts.shift] ||= {} # rubocop:disable IndentationWidth
157
+ while key_parts.size > 1 && (part = key_parts.shift)
158
+ ptr = ptr[part] ||= {} # rubocop:disable IndentationWidth
159
+ end
160
+
161
+ key = key_parts.shift
162
+ case ptr[key]
163
+ when nil then ptr[key] = value
164
+ when Array then ptr[key] << value
165
+ else ptr[key] = [ptr[key], value].flatten
166
+ end
165
167
 
166
- def git
167
- @repository.git
168
+ hash
169
+ end
170
+ end
171
+
172
+ def git
173
+ @repository.git
174
+ end
168
175
  end
169
176
  end
170
177
  end