8

我有a) 一个没有该目录的工作.git目录和b) 一个存储库。a是历史中间的一些修改b

我怎样才能找出哪个版本a匹配b

我想到了一个 shellscriptdiff从工作目录到所有修订版,然后选择差异最小(希望为 0)的那个。

那会有点原始(我不知道该怎么做),有没有更简单的方法?

4

4 回答 4

4

您可以编写一个脚本来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
于 2011-10-15T19:02:30.503 回答
1

您可以减少必须用检查的修订数量。将您的工作目录与最新版本进行比较,并选择一些看起来尽可能少的不同行。假设您的最新版本有一行包含foobar但您的工作目录没有;运行git log -Sfoobar输出所有添加或删除的提交foobar。您现在可以将您的存储库移回该列表中的第一个(最新)修订版,因为该版本之后的所有修订版都将与您的工作目录不同。重复另一个差异,直到找到正确的修订。

于 2011-10-15T17:28:33.963 回答
1

由于 git 使用内容可寻址的文件存储,因此应该可以在某处找到任意树,但我不知道细节。我猜您可以将分离的工作目录中的文件复制到存储库的工作目录中,然后提交所有内容,以某种方式找出提交创建的树对象的哈希,并在现有提交中搜索引用同一棵树的提交.

为此,树显然需要完美匹配,因此您不能将任何未跟踪的文件放入提交中(例如目标文件、编辑器备份等)。

编辑:我只是在一个存储库上尝试过这个(git cat-file commit HEAD在 HEAD 处显示树对象,并搜索该git log --pretty=raw树哈希的输出),但它没有用(我在历史记录中没有找到哈希)。当我提交时,我确实收到了一堆关于 CRLF 转换的警告,所以这可能是问题所在,也就是说,根据你的 git 配置为破坏文本文件的方式,你可能会为同一棵树获得不同的哈希值。我正在标记这个答案社区维基,以防有人知道如何可靠地做到这一点。

于 2011-10-15T17:40:04.697 回答
0

假设 in-tree 和b/.gitignore 设置与创建提交时的设置相同,并且工作树中没有任何未忽略的未跟踪文件,您应该能够运行这样的东西。

策略是重新创建工作树的 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
于 2011-10-15T20:06:59.077 回答