今天一位同事找到我说到,如何能快速的定位一个文件的某个版本是在哪一次历史提交里引入的。
解决方案如下:
首先根据文件内容确定该文件对应的blob对象的object ID
git hash-object path/to/file
或者直接使用几个shell命令生成:
(echo -e -n "blob $(wc -c path/to/file | awk '{print $1}')\0" ; cat path/to/file) | shasum
得到对应blob对象的object ID以后,使用whatchanged查看该文件的变更历史,默认情况下whatchanged不会输出merge中修改的文件,为了避免遗漏,此处还是用-m来告诉whatchanged还要输出merge的结果:
git whatchanged -m -- path/to/file
此时可以搜索刚刚得到的blob的object ID。需要注意的是由于whatchanged默认显示blob变化信息时只使用blob ID的前7位,因此搜索时也要注意不能使用完整的object ID,也应该使用object ID的前几位。
ruby写了个脚本实现查找:
注:你看到此文章时,该脚本可能已经不是最新版本,需要最新的code请移步
loveky's GITHUB
#!/usr/bin/ruby
# Author: loveky <ylzcylx@gmail.com>
# Blog : http://loveky2012.blogspot.com
#Usage: find_it_in_history.rb --file <file> --repo <path/to/repo> --path_in_repo <file/path/in/repo>
require 'digest/sha1'
require 'optparse'
found = false
commit = nil
options = {}
OptionParser.new do |opts|
opts.banner = "Find the commit who first introduced the specified version of a file"
opts.on('--file FILE', 'Which file to check') do |value|
options[:file] = value
end
opts.on('--repo REPO', 'which repo to check') do |value|
options[:repo] = value
end
opts.on('--path_in_repo PATH', 'the path of the file in repo') do |value|
options[:path_in_repo] = value
end
end.parse!
file_content = File.open(options[:file]) {|f| f.read}
file_sha1 = Digest::SHA1.hexdigest("blob #{file_content.length}\0" + file_content)
puts("#{options[:file]} => #{file_sha1}")
Dir.chdir(options[:repo])
IO.popen("git whatchanged --oneline -m -- #{options[:path_in_repo]}") do |git_whatchanged|
git_whatchanged.each_line do |line|
if line[0,1] == ':'
new_blob = (/\.\.\. ([0-9a-fA-F]{7})\.\.\./.match(line))[1]
if new_blob == file_sha1[0,7]
puts "Found blob(#{file_sha1[0,7]}) in commit " + commit
found = true
elsif found == true
exit 0
end
else
commit = line[0,7]
end
end
end