Как удалить запись с нулевым sha1 в Git-дереве
Я унаследовал репозиторий git с нулевым sha1 для записи коммита в дереве, что не позволило FishEye проиндексировать репозиторий.
$ git fsck
Checking object directoriies: 100%(256/256), done.
warning in tree db22a67df70dc4ff90ec4cd666da91e9c2cb0d9:
contains entries pointing to null sha1
Checking objects: 100% (416532/416532), done.
Checking connectivity: 416532, done.
Поиск данного дерева дает мне следующий результат:
$ git ls-tree db22a6
100644 blob e615f18b55a39f2719112ce209c2505dd92d8e75 .gitignore
100644 blob ac852f06c5a04420356c1d5efca44d9a864e78b0 .project
160000 commit 0000000000000000000000000000000000000000 SomeDirectory
100644 blob 631c17e28026261a2ccf6bc570842cf4af9f181c GoDeploy.bat
100644 blob 40e992ab5c3868af2910135c3ac4610c3646e7f8 pom.xml
Глядя в историю, я обнаружил, что SomeDirectory
изначально был подмодулем git и что коммит, который, кажется, вызывает проблему, является тем, который удалил оба .gitmodules
а также SomeDirectory
, Теперь есть реальный каталог под названием SomeDirectory
в том же месте, где был виновник.
Я, хотя я все еще мог попытаться исправить запустить git filter-branch
чтобы посмотреть, что я в итоге, но это не работает
$ git filter-branch --force --index-filter \
$ 'git rm --cached --ignore-unmatch SomeDirectory' \
$ --prune-empty --tag-name-filter cat -- --all
[... striped out for clarity]
Rewrite c571a3ec94e9f84471577bac41ac7375c729ef08 (76/18522)error:
cache enttry has null sha1: SomeDirectory
fatal: unable to write new index file
Could not initialize the index
[... striped out for clarity]
Что я должен попробовать сделать дальше, зная, что не существует резервной копии, о которой я знаю до фиксации, вызывающей проблему.
3 ответа
Полученное сообщение говорит о том, что было только одно дерево с плохим подмодулем. В этом случае вам очень мало что нужно убирать. Вы можете создать новое фиксированное дерево, у которого нет этой проблемы:
$ git ls-tree db22a67df70dc4ff90ec4cd666da91e9c2cb0d9 | > sed -e '/ 0 \ {40 \} / d' | > мерзавец (новое дерево SHA1 здесь)
Ваш вопрос показывает git ls-tree
выход уже. sed
удаляет строку с плохим подмодулем и git mktree
создает новый объект дерева из результата.
Когда у вас есть фиксированное дерево, вы можете создать фиксированный коммит, используя это дерево:
$ git cat-file commit c571a3ec94e9f84471577bac41ac7375c729ef08 | > sed 's / db22a67df70dc4ff90ec4cd666da91e9c2cb0d9 / (новое дерево SHA1 здесь)/' | > git hash-object -t commit -w --stdin (новый коммит SHA1 здесь)
git cat-file commit c571a3ec94e9f84471577bac41ac7375c729ef08
печатает проблемный объект коммита в текстовой форме. Начнется с tree db22a67df70dc4ff90ec4cd666da91e9c2cb0d9
и продолжается с остальной информацией о коммите (родитель, автор, коммиттер, сообщение о коммите). sed
заменяет tree
ссылка строки на старое дерево новым. git hash-object -t commit -w --stdin
создает новый результат фиксации из результата, записывает его в хранилище и печатает его ID.
Когда у вас есть фиксированный коммит, вы можете использовать git replace
:
$ git replace c571a3ec94e9f84471577bac41ac7375c729ef08 (новый коммит SHA1 здесь)
На самом деле это еще ничего не меняет, но говорит Git, что всякий раз, когда он будет читать коммит c571a3ec94e9f84471577bac41ac7375c729ef08
, вместо этого он должен прочитать новый объект коммита.
И, наконец, использовать git filter-branch
сделать его постоянным. Он проходит через все коммиты, читает их и записывает обратно. Обычно, без каких-либо опций для изменения коммитов, это не будет иметь большого эффекта, но из-за более раннего git replace
это вызывает все коммиты с c571a3ec94e9f84471577bac41ac7375c729ef08
как родитель, который будет переписан для ссылки на новый коммит вместо всех коммитов, которые также относятся к переписанному и т. д.
Для тех, у кого все еще есть проблемы с этим, я решил эту проблему с помощью git-filter-repo:
git filter-repo --path <folder> --invert-paths
filter-repo не имеет тех же проблем, что и filter-branch с нулевым sha1, и работает намного быстрее.
См. Этот ответ / вопрос: /questions/9494276/udalit-papku-i-ee-soderzhimoe-iz-istorii-gitgithub/55357983#55357983
Возможно, он будет работать с интерактивным перебазированием, чтобы изменить коммит, содержащий проблемную ссылку на коммит SomeDirectory, например
$ git branch backup_branch # To be able to revert if not satisfied
$ git rebase -i db22a6^ # From parent to db22a6
...
# You then select Edit for commit db22a6 in the editor
...
$ git reset HEAD^ # Reset the commit db22a6 but not its changes
$ git status
...
# should list as modified: .gitignore .project SomeDirectory GoDeploy.bat pom.xml
...
$ git checkout SomeDirectory # Cancel the troublesome change
$ git add .gitignore .project GoDeploy.bat pom.xml
$ git commit -m "your commit message"
$ git rebase --continue