#!/usr/bin/env ruby # Usage: # auto-style.rb oldrev newrev [pushref] require 'shellwords' require 'tmpdir' ENV['LC_ALL'] = 'C' class Git attr_reader :depth def initialize(oldrev, newrev, branch = nil) @oldrev = oldrev @newrev = !newrev || newrev.empty? ? 'HEAD' : newrev @branch = branch return unless oldrev # GitHub may not fetch github.event.pull_request.base.sha at checkout git('log', '--format=%H', '-1', @oldrev, out: IO::NULL, err: [:child, :out]) or git('fetch', '--depth=1', 'origin', @oldrev) git('log', '--format=%H', '-1', "#@newrev~99", out: IO::NULL, err: [:child, :out]) or git('fetch', '--depth=100', 'origin', @newrev) with_clean_env do @revs = {} IO.popen(['git', 'log', '--format=%H %s', "#{@oldrev}..#{@newrev}"]) do |f| f.each do |line| line.chomp! rev, subj = line.split(' ', 2) @revs[rev] = subj end end @depth = @revs.size end end # ["foo/bar.c", "baz.h", ...] def updated_paths with_clean_env do IO.popen(['git', 'diff', '--name-only', @oldrev, @newrev], &:readlines).each(&:chomp!) end end # [0, 1, 4, ...] def updated_lines(file) # NOTE: This doesn't work well on pull requests, so not used anymore lines = [] revs = @revs.map {|rev, subj| rev unless subj.start_with?("Revert ")}.compact revs_pattern = /\A(?:#{revs.join('|')}) / with_clean_env { IO.popen(['git', 'blame', '-l', '--', file], &:readlines) }.each_with_index do |line, index| if revs_pattern =~ line lines << index end end lines end def commit(log, *files) git('add', *files) git('commit', '-m', log) end def push git('push', 'origin', @branch) if @branch end def diff git('--no-pager', 'diff') end private def git(*args, **opts) cmd = ['git', *args] puts "+ #{cmd.shelljoin}" ret = with_clean_env { system(*cmd, **opts) } unless ret or opts[:err] abort "Failed to run: #{cmd}" end ret end def with_clean_env git_dir = ENV.delete('GIT_DIR') # this overcomes '-C' or pwd yield ensure ENV['GIT_DIR'] = git_dir if git_dir end end DEFAULT_GEM_LIBS = %w[ bundler cmath csv e2mmap fileutils forwardable ipaddr irb logger matrix mutex_m ostruct prime rdoc rexml rss scanf shell sync thwait tracer webrick ] DEFAULT_GEM_EXTS = %w[ bigdecimal date dbm digest etc fcntl fiddle gdbm io/console io/nonblock json openssl psych racc sdbm stringio strscan zlib ] IGNORED_FILES = [ # default gems whose master is GitHub %r{\Abin/(?!erb)\w+\z}, *(DEFAULT_GEM_LIBS + DEFAULT_GEM_EXTS).flat_map { |lib| [ %r{\Alib/#{lib}/}, %r{\Alib/#{lib}\.gemspec\z}, %r{\Alib/#{lib}\.rb\z}, %r{\Atest/#{lib}/}, ] }, *DEFAULT_GEM_EXTS.flat_map { |ext| [ %r{\Aext/#{ext}/}, %r{\Atest/#{ext}/}, ] }, # vendoring (ccan) %r{\Accan/}, # vendoring (io/) %r{\Aext/io/}, # vendoring (nkf) %r{\Aext/nkf/nkf-utf8/}, # vendoring (onigmo) %r{\Aenc/}, %r{\Ainclude/ruby/onigmo\.h\z}, %r{\Areg.+\.(c|h)\z}, # explicit or implicit `c-file-style: "linux"` %r{\Aaddr2line\.c\z}, %r{\Amissing/}, %r{\Astrftime\.c\z}, %r{\Avsnprintf\.c\z}, # to respect the original statements of licenses %r{\ALEGAL\z}, # trailing spaces could be intentional in TRICK code %r{\Asample/trick[^/]*/}, ] DIFFERENT_STYLE_FILES = %w[ addr2line.c io_buffer.c prism*.c scheduler.c ] def adjust_styles(files) trailing = eofnewline = expandtab = indent = false edited_files = files.select do |f| src = File.binread(f) rescue next eofnewline = eofnewline0 = true if src.sub!(/(?