Skip to content

Commit d88c6c7

Browse files
jawshooahsds
authored andcommitted
Add pre-commit hook to check for case conflicts
Files with the same name but different capitalization can cause trouble on case-insensitive filesystems. Change-Id: Ic860f23f28a8c1bc723aac010fab401ad0738125 Reviewed-on: http://gerrit.causes.com/48150 Tested-by: jenkins <jenkins@brigade.com> Reviewed-by: Shane da Silva <shane.dasilva@brigade.com>
1 parent d644a86 commit d88c6c7

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

config/default.yml

+4
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ PreCommit:
104104
- 'Gemfile.lock'
105105
- '*.gemspec'
106106

107+
CaseConflicts:
108+
description: 'Checking for case-insensitivity conflicts'
109+
quiet: true
110+
107111
ChamberSecurity:
108112
enabled: false
109113
description: 'Checking that settings have been secured with Chamber'

lib/overcommit/git_repo.rb

+13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ def modified_files(options)
5454
map { |relative_file| File.expand_path(relative_file) }
5555
end
5656

57+
# Returns the names of files in the given paths that are tracked by git.
58+
#
59+
# @param paths [Array<String>] list of paths to check
60+
# @option options [String] ref ('HEAD') Git ref to check
61+
# @return [Array<String>] list of absolute file paths
62+
def list_files(paths = [], options = {})
63+
ref = options[:ref] || 'HEAD'
64+
`git ls-tree --name-only #{ref} #{paths.join(' ')}`.
65+
split(/\n/).
66+
map { |relative_file| File.expand_path(relative_file) }.
67+
reject { |file| File.directory?(file) } # Exclude submodule directories
68+
end
69+
5770
# Returns the names of all files that are tracked by git.
5871
#
5972
# @return [Array<String>] list of absolute file paths
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Overcommit::Hook::PreCommit
2+
# Checks for files that would conflict in case-insensitive filesystems
3+
# Adapted from https://github.com/pre-commit/pre-commit-hooks
4+
class CaseConflicts < Base
5+
def run
6+
paths = Set.new(applicable_files.map { |file| File.dirname(file) + File::SEPARATOR })
7+
repo_files = Set.new(Overcommit::GitRepo.list_files(paths.to_a) + applicable_files)
8+
conflict_hash = repo_files.classify(&:downcase).
9+
select { |_, files| files.size > 1 }
10+
conflict_files = applicable_files.
11+
select { |file| conflict_hash.include?(file.downcase) }
12+
13+
conflict_files.map do |file|
14+
conflicts = conflict_hash[file.downcase].map { |f| File.basename(f) }
15+
msg = "Conflict detected for case-insensitive file systems: #{conflicts.join(', ')}"
16+
Overcommit::Hook::Message.new(:error, file, nil, msg)
17+
end
18+
end
19+
end
20+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require 'spec_helper'
2+
3+
describe Overcommit::Hook::PreCommit::CaseConflicts do
4+
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
5+
let(:context) { double('context') }
6+
subject { described_class.new(config, context) }
7+
8+
before do
9+
Overcommit::GitRepo.stub(:list_files).and_return(%w[foo])
10+
end
11+
12+
context 'when a new file conflicts with an existing file' do
13+
before do
14+
subject.stub(:applicable_files).and_return(%w[Foo])
15+
end
16+
17+
it { should fail_hook }
18+
end
19+
20+
context 'when a new file conflicts with another new file' do
21+
before do
22+
subject.stub(:applicable_files).and_return(%w[bar Bar])
23+
end
24+
25+
it { should fail_hook }
26+
end
27+
28+
context 'when there are no conflicts' do
29+
before do
30+
subject.stub(:applicable_files).and_return(%w[bar baz])
31+
end
32+
33+
it { should pass }
34+
end
35+
end

0 commit comments

Comments
 (0)