diff options
Diffstat (limited to 'lib/gitlab/git/attributes_parser.rb')
-rw-r--r-- | lib/gitlab/git/attributes_parser.rb | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/lib/gitlab/git/attributes_parser.rb b/lib/gitlab/git/attributes_parser.rb new file mode 100644 index 00000000000..d8aeabb6cba --- /dev/null +++ b/lib/gitlab/git/attributes_parser.rb @@ -0,0 +1,115 @@ +module Gitlab + module Git + # Class for parsing Git attribute files and extracting the attributes for + # file patterns. + class AttributesParser + def initialize(attributes_data) + @data = attributes_data || "" + + if @data.is_a?(File) + @patterns = parse_file + end + end + + # Returns all the Git attributes for the given path. + # + # file_path - A path to a file for which to get the attributes. + # + # Returns a Hash. + def attributes(file_path) + absolute_path = File.join('/', file_path) + + patterns.each do |pattern, attrs| + return attrs if File.fnmatch?(pattern, absolute_path) + end + + {} + end + + # Returns a Hash containing the file patterns and their attributes. + def patterns + @patterns ||= parse_file + end + + # Parses an attribute string. + # + # These strings can be in the following formats: + # + # text # => { "text" => true } + # -text # => { "text" => false } + # key=value # => { "key" => "value" } + # + # string - The string to parse. + # + # Returns a Hash containing the attributes and their values. + def parse_attributes(string) + values = {} + dash = '-' + equal = '=' + binary = 'binary' + + string.split(/\s+/).each do |chunk| + # Data such as "foo = bar" should be treated as "foo" and "bar" being + # separate boolean attributes. + next if chunk == equal + + key = chunk + + # Input: "-foo" + if chunk.start_with?(dash) + key = chunk.byteslice(1, chunk.length - 1) + value = false + + # Input: "foo=bar" + elsif chunk.include?(equal) + key, value = chunk.split(equal, 2) + + # Input: "foo" + else + value = true + end + + values[key] = value + + # When the "binary" option is set the "diff" option should be set to + # the inverse. If "diff" is later set it should overwrite the + # automatically set value. + values['diff'] = false if key == binary && value + end + + values + end + + # Iterates over every line in the attributes file. + def each_line + @data.each_line do |line| + break unless line.valid_encoding? + + yield line.strip + end + end + + private + + # Parses the Git attributes file. + def parse_file + pairs = [] + comment = '#' + + each_line do |line| + next if line.start_with?(comment) || line.empty? + + pattern, attrs = line.split(/\s+/, 2) + + parsed = attrs ? parse_attributes(attrs) : {} + + absolute_pattern = File.join('/', pattern) + pairs << [absolute_pattern, parsed] + end + + # Newer entries take precedence over older entries. + pairs.reverse.to_h + end + end + end +end |