如何仅对分阶段内容运行git预提交检查?

时间:2012-12-08 18:49:26

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

假设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所建议的那样,我在运行样式检查之前存储了非分段文件,然后将其弹出。但是,如果只暂存文件中的某些更改,则在预提交挂钩结束时弹出存储时会导致合并冲突。

有什么更好的方法可以做到这一点?我想针对即将提交的文件的分阶段版本运行样式检查。

3 个答案:

答案 0 :(得分:10)

我会避免在挂钩中自动使用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 $?

答案 1 :(得分:5)

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

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

...和git stash pop -q with:

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

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

答案 2 :(得分:2)

如何在没有存储的情况下进行提交

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

关键位

  1. git checkout-index -a --prefix={{temp_dir}}
  2. 它可能/可能不会比存储更慢/占用更多空间,但与索引混淆的自动化似乎本质上是脆弱的。也许需要一个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