Как удалить запись с нулевым 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
Другие вопросы по тегам