我有a
) 一个没有该目录的工作.git
目录和b
) 一个存储库。a
是历史中间的一些修改b
。
我怎样才能找出哪个版本a
匹配b
?
我想到了一个 shellscriptdiff
从工作目录到所有修订版,然后选择差异最小(希望为 0)的那个。
那会有点原始(我不知道该怎么做),有没有更简单的方法?
我有a
) 一个没有该目录的工作.git
目录和b
) 一个存储库。a
是历史中间的一些修改b
。
我怎样才能找出哪个版本a
匹配b
?
我想到了一个 shellscriptdiff
从工作目录到所有修订版,然后选择差异最小(希望为 0)的那个。
那会有点原始(我不知道该怎么做),有没有更简单的方法?
您可以编写一个脚本来diff gitdir workdir | wc -c
为每个提交运行。然后您可以整理结果并说具有最小差异(由 衡量wc -c
)的提交是最接近裸工作目录的提交。
这是它在 Python 中的样子:
find_closest_sha1.py:
#!/usr/bin/env python
import subprocess
import shlex
import sys
import os
import operator
gitdir,workdir=map(os.path.realpath,sys.argv[1:3])
os.chdir(gitdir)
proc=subprocess.Popen(shlex.split('git rev-list --all'),stdout=subprocess.PIPE)
shas,err=proc.communicate()
shas=shas.split()
head=shas[0]
data={}
for sha1 in shas:
subprocess.Popen(shlex.split('git checkout {s}'.format(s=sha1)),
stderr=open('/dev/null')).wait()
proc=subprocess.Popen(shlex.split('diff {g} {w}'.format(g=gitdir,w=workdir)),
stdout=subprocess.PIPE)
out,err=proc.communicate()
distance=len(out)
data[sha1]=distance
answer=min(data.items(),key=operator.itemgetter(1))[0]
print('closest match: {s}'.format(s=answer))
subprocess.Popen(shlex.split('git checkout {h}'.format(h=head)),
stderr=open('/dev/null')).wait()
例子:
% rsync -a gitdir/ workdir/
% cd workdir
% git checkout HEAD~10
HEAD is now at b9fcebf... fix foo
% cd ..
% /bin/rm -rf workdir/.git
% find_closest_sha1.py gitdir workdir
closest match: b9fcebfb170785c19390ebb4a9076d11350ade79
您可以减少必须用镐检查的修订数量。将您的工作目录与最新版本进行比较,并选择一些看起来尽可能少的不同行。假设您的最新版本有一行包含foobar
但您的工作目录没有;运行git log -Sfoobar
输出所有添加或删除的提交foobar
。您现在可以将您的存储库移回该列表中的第一个(最新)修订版,因为该版本之后的所有修订版都将与您的工作目录不同。重复另一个差异,直到找到正确的修订。
由于 git 使用内容可寻址的文件存储,因此应该可以在某处找到任意树,但我不知道细节。我猜您可以将分离的工作目录中的文件复制到存储库的工作目录中,然后提交所有内容,以某种方式找出提交创建的树对象的哈希,并在现有提交中搜索引用同一棵树的提交.
为此,树显然需要完美匹配,因此您不能将任何未跟踪的文件放入提交中(例如目标文件、编辑器备份等)。
编辑:我只是在一个存储库上尝试过这个(git cat-file commit HEAD
在 HEAD 处显示树对象,并搜索该git log --pretty=raw
树哈希的输出),但它没有用(我在历史记录中没有找到哈希)。当我提交时,我确实收到了一堆关于 CRLF 转换的警告,所以这可能是问题所在,也就是说,根据你的 git 配置为破坏文本文件的方式,你可能会为同一棵树获得不同的哈希值。我正在标记这个答案社区维基,以防有人知道如何可靠地做到这一点。
假设 in-tree 和b/.git
ignore 设置与创建提交时的设置相同,并且工作树中没有任何未忽略的未跟踪文件,您应该能够运行这样的东西。
策略是重新创建工作树的 git id,然后搜索包含该树的任何提交。
# work from detached working tree
cd a
# Use existing repository and a temporary index file
GIT_DIR=b/.git
GIT_INDEX_FILE=/tmp/tmp-index
export GIT_DIR GIT_INDEX_FILE
# find out the id of the current working tree
git add . &&
tree_id=$(git write-tree) &&
rm /tmp/tmp-index
# find a commit that matches the tree
for commit in $(git rev-list --all)
do
if test "$tree_id" = "$(git rev-parse ${commit}^{tree})"; then
git show "$commit"
break
fi
done
unset GIT_DIR
unset GIT_INDEX_FILE