【问题标题】:How can I run git pre-commit checks only on staged content?如何仅对暂存内容运行 git pre-commit 检查?
【发布时间】:2012-11-26 16:06:18
【问题描述】:

假设git status 给出这个:

# On branch X
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   file1.cc
#   modified:   file1.h
#   modified:   file1_test.cc
#   modified:   SConscript
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#   (commit or discard the untracked or modified content in submodules)
#
#   modified:   file1.cc
#   modified:   tinyxml2 (untracked content)
#

在这种情况下,只有对 file1.cc 所做的部分更改已暂存/为下一次提交编制索引。

我运行了一个预提交脚本来运行样式检查器:

#!/bin/bash                                                                                                                                                                     

git stash -q --keep-index

# Do the checks                                                                                                                                                                 
RESULT=0
while read status file
do
    if python ~/python/cpplint.py "$file"; then
        let RESULT=1
    fi
done < <(git diff --cached --name-status --diff-filter=ACM | grep -P  '\.((cc)|(h)|(cpp)|(c))$' )

git stash pop -q

[ $RESULT -ne 0 ] && exit 1
exit 0

按照here 的建议,我在运行样式检查之前存储未暂存的文件,然后将它们弹出。但是,在仅暂存文件中的某些更改的情况下,当我在预提交挂钩的末尾弹出存储时,这会导致合并冲突。

有什么更好的方法来做到这一点?我想对即将提交的文件的暂存版本运行样式检查。

【问题讨论】:

标签: git pre-commit-hook pre-commit


【解决方案1】:

我会避免在挂钩中自动使用git stash。我发现可以使用git show ':filename' 来获取隐藏文件的内容。
相反,我使用了下一种方法:

git diff --cached --name-only --diff-filter=ACMR | while read filename; do
    git show ":$filename" | GIT_VERIFY_FILENAME="$filename" verify_copyright \
        || exit $?
done \
    || exit $?

【讨论】:

    【解决方案2】:

    git stash -q --keep-index 替换为:

    git diff --full-index --binary > /tmp/stash.$$
    git stash -q --keep-index
    

    ...和git stash pop -q 与:

    git apply --whitespace=nowarn < /tmp/stash.$$` && git stash drop -q
    rm /tmp/stash.$$
    

    这会将差异保存到一个临时文件并在最后使用git apply 重新应用它。更改仍然冗余保存到存储区(稍后将被删除),因此如果重新应用出现问题,您可以使用 git stash show -p 对其进行检查,而无需查找临时文件。

    【讨论】:

      【解决方案3】:

      如何在不存储的情况下控制提交

      我们使用这个.git/hooks/pre-commit 来检查一个原子语法包

      密钥位

      1. git checkout-index -a --prefix={{temp_dir}}

      它可能/可能不会比存储慢得多/占用更多空间,但是与索引混淆的自动化似乎天生就很脆弱。也许需要一个 git contrib 脚本来创建软/硬链接树、最小空间只读、临时索引签出、促进更好/更快的.git/hooks/pre-commit(或者,比如说,.git/hooks/pre-commit-index)脚本,以便不需要工作目录的完整第二个副本,只需要工作目录->索引更改。

      #!/usr/bin/env ruby
      require 'tmpdir'
      autoload :FileUtils,  'fileutils'
      autoload :Open3,      'open3'
      autoload :Shellwords, 'shellwords'
      
      # ---- setup
      
      INTERACTIVE         = $stdout.tty? || $stderr.tty?
      DOT                 = -'.'
      BLOCK_SIZE          = 4096
      TEMP_INDEX_DIR      = Dir.mktmpdir
      TEMP_INDEX_DIR_REAL = File.realpath(TEMP_INDEX_DIR)
      
      def cleanup
        FileUtils.remove_entry(TEMP_INDEX_DIR) if File.exist? TEMP_INDEX_DIR
      end
      
      at_exit { cleanup }
      
      %w[INT TERM PIPE HUP QUIT].each do |sig|
        Signal.trap(sig) { cleanup }
      end
      
      # ---- functions
      
      def fix_up_dir_output(data)
        data.gsub! TEMP_INDEX_DIR_REAL, DOT
        data.gsub! TEMP_INDEX_DIR, DOT
        data
      end
      
      def sh(*args)
        Open3.popen3(*args) do |_, stdout, stderr, w_thr|
          files = [stdout, stderr]
          until files.empty? do
            if ready = IO.select(files)
              ready[0].each do |f|
                begin
                  data = f.read_nonblock BLOCK_SIZE
                  data = fix_up_dir_output data
                  if f.fileno == stderr.fileno
                    $stderr.write data
                  else
                    $stdout.write data
                  end
                rescue EOFError
                  files.delete f
                end
              end
            end
          end
          if !(done = w_thr.value).success?
            exit(done.exitstatus)
          end
        end
      end
      
      def flags(args)
        skip = false
        r = []
        args.each do |a|
          if a[0] == '-' && !skip
            a.slice! 0
            if a[0] == '-'
              skip ||= a[1].nil?
              a.slice! 0
              r << a unless a.empty?
            else # -[^-]+
              r += a.split ''
            end
          end
        end
        r
      end
      
      def less_lint
        args = %w[lessc --lint]
        args << '--no-color' unless INTERACTIVE
        args << 'index.less'
        sh(*args)
      end
      
      def ensure_git_commit_signed
        pcmd = `ps -wwp#{Process.ppid} -ocommand=`.chop
        args = flags(Shellwords.split(pcmd)[1..-1])
        return unless (args & %w[gpg-sign S]).empty?
        $stderr.puts 'All git commits must be GPG-signed'
        $stderr.puts "    command: #{pcmd}"
        exit 1
      end
      
      # ---- main
      
      # 1. make sure all commits are signed
      ensure_git_commit_signed
      
      # 2. check files that are in the index
      sh 'git', 'checkout-index', '-a', "--prefix=#{TEMP_INDEX_DIR}/"
      Dir.chdir TEMP_INDEX_DIR do
        # 3. make sure all commits contain only legal .less files
        less_lint
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-18
        • 2015-08-19
        • 2015-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多