From 8c6f250dcf4624892347e7d15c9739c043528abf Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 18 Apr 2025 06:56:41 +0900 Subject: [PATCH] Copy over bin/auto-style.rb from ruby/git.ruby-lang.org as of: https://github.com/ruby/git.ruby-lang.org/commit/a8635a4cd93c8adc2e1b48b6ae8fa3fcd3bb4b34 --- tool/auto-style.rb | 239 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 tool/auto-style.rb diff --git a/tool/auto-style.rb b/tool/auto-style.rb new file mode 100644 index 0000000000..8dc7e1bdee --- /dev/null +++ b/tool/auto-style.rb @@ -0,0 +1,239 @@ +#!/usr/bin/env ruby +# Usage: +# auto-style.rb [repo_path] [args...] + +require 'shellwords' +require 'tmpdir' +ENV['LC_ALL'] = 'C' + +class Git + attr_reader :depth + + def initialize(oldrev, newrev, branch) + @oldrev = oldrev + @newrev = newrev + @branch = branch + 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) + 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) + git('push', 'origin', @branch) + end + + private + + def git(*args) + cmd = ['git', *args].shelljoin + unless with_clean_env { system(cmd) } + abort "Failed to run: #{cmd}" + end + 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[^/]*/}, +] + +repo_path, *rest = ARGV +rest.each_slice(3).map do |oldrev, newrev, refname| + branch = IO.popen({ 'GIT_DIR' => repo_path }, ['git', 'rev-parse', '--symbolic', '--abbrev-ref', refname], &:read).strip + next if branch != 'master' # Stable branches are on svn, and for consistency we should not make a git-specific commit. + vcs = Git.new(oldrev, newrev, branch) + + Dir.mktmpdir do |workdir| + depth = vcs.depth + 1 + system "git clone --depth=#{depth} --branch=#{branch} file:///#{repo_path} #{workdir}" + Dir.chdir(workdir) + + paths = vcs.updated_paths + paths.select! {|l| + /^\d/ !~ l and /\.bat\z/ !~ l and + (/\A(?:config|[Mm]akefile|GNUmakefile|README)/ =~ File.basename(l) or + /\A\z|\.(?:[chsy]|\d+|e?rb|tmpl|bas[eh]|z?sh|in|ma?k|def|src|trans|rdoc|ja|en|el|sed|awk|p[ly]|scm|mspec|html|)\z/ =~ File.extname(l)) + } + files = paths.select {|n| File.file?(n) } + files.reject! do |f| + IGNORED_FILES.any? { |re| f.match(re) } + end + next if files.empty? + + trailing = eofnewline = expandtab = false + + edited_files = files.select do |f| + src = File.binread(f) rescue next + eofnewline = eofnewline0 = true if src.sub!(/(?