假设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所建议的那样,我在运行样式检查之前存储了非分段文件,然后将其弹出。但是,如果只暂存文件中的某些更改,则在预提交挂钩结束时弹出存储时会导致合并冲突。
有什么更好的方法可以做到这一点?我想针对即将提交的文件的分阶段版本运行样式检查。
答案 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
来检查原子语法包
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