Найти Git-ревизию рабочего каталога, отсутствующего в каталоге .git - PullRequest
8 голосов
/ 15 октября 2011

У меня есть a) рабочий каталог без каталога .git и b) хранилища.a - это какая-то ревизия в середине истории b.

Как узнать, какая ревизия a совпадает с b?

Я думал оShellscript делает diff из рабочего каталога для всех ревизий и выбирает тот с наименьшими (надеюсь, 0) различиями.

Это было бы немного необработанным (и я не уверен, как это сделать)Есть ли более простой способ?

Ответы [ 4 ]

4 голосов
/ 15 октября 2011

Вы можете написать скрипт для запуска 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
1 голос
/ 15 октября 2011

Так как git использует хранилище файлов с адресацией по содержимому, там должно быть возможно найти произвольное дерево где-то там, но я не знаю деталей.Я предполагаю, что вы могли бы скопировать файлы из отдельного рабочего каталога в рабочий каталог хранилища, затем зафиксировать все, каким-то образом выяснить хеш объекта дерева, созданного коммитом, и найти в существующих коммитах тот, который ссылается на то же дерево..

Чтобы это работало, дерево, очевидно, должно идеально совпадать, поэтому вы не должны включать в коммит какие-либо неотслеживаемые файлы (например, объектные файлы, резервные копии редактора и т. Д.).

Редактировать: я только что попробовал это на одном репозитории (с git cat-file commit HEAD, чтобы показать объект дерева в HEAD, и поиск в выводе git log --pretty=raw для этого хеша дерева), и это не сработало (я не нашелхеш в истории).Я получил кучу предупреждений о преобразовании CRLF, когда делал коммит, так что это могло быть проблемой, т.е. вы, вероятно, получаете разные хеши для одного и того же дерева в зависимости от того, как ваш git настроен для манипулирования текстовыми файлами.Я отмечаю этот ответ вики-сообществом на случай, если кто-то знает, как это сделать надежно.

1 голос
/ 15 октября 2011

Вы можете сократить количество проверок, которые необходимо проверить, с помощью кирки . Различайте свой рабочий каталог с последней ревизией и выберите какую-то отличающуюся строку, которая выглядит как можно реже. Скажем, в вашей последней ревизии есть строка, содержащая foobar, а в вашем рабочем каталоге - нет; запустить git log -Sfoobar, который выводит все коммиты, добавляя или удаляя foobar. Теперь вы можете переместить свой репозиторий обратно к первой (последней) ревизии в этом списке, поскольку все ревизии после этой будут отличаться от вашей рабочей директории. Повторите с другим отличием, пока не найдете правильную версию.

0 голосов
/ 16 октября 2011

Предполагая, что настройки игнорирования внутри дерева и b/.git такие же, какими они были при создании коммита, и что в рабочем дереве нет игнорируемых неотслеживаемых файлов, вы должны иметь возможность запускать что-то подобное.

Стратегия заключается в том, чтобы воссоздать git-идентификатор рабочего дерева и затем выполнить поиск любого коммита, содержащего это дерево.

# 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
...