我有a
)一个工作目录,没有.git
目录和b
)一个存储库。a
是b
历史中期的一次修正。
如何找出a
与b
中哪个版本匹配?
我想到了一个shellscript做diff
从工作目录到所有的修订,并选择一个最小的差异(希望0)。
那就有点粗糙了(我不知道怎么做),有更简单的方法吗?
您可以编写一个脚本为每次提交运行diff gitdir workdir | wc -c
。然后,您可以整理结果,并说具有最小差异的提交(由wc -c
测量)是最接近裸工作目录的提交。
在Python中可能是这样的:
find_closestrongha1.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
忽略设置与创建提交时一样,并且在工作树中没有任何非忽略的未跟踪文件,您应该能够运行这样的操作。
策略是重新创建工作树的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