У нас есть большой репозиторий git, который я хочу отправить в отдельный экземпляр gitlab.
Проблема в том, что удаленный gitlab не позволяет мне нажимать мое репо:
git push --mirror https://mygitlab/xy/myrepo.git
Это даст мне эту ошибку:
Enumerating objects: 1383567, done.
Counting objects: 100% (1383567/1383567), done.
Delta compression using up to 8 threads
Compressing objects: 100% (207614/207614), done.
remote: error: object c05ac7f76dcd3e8fb3b7faf7aab9b7a855647867:
duplicateEntries: contains duplicate file entries
remote: fatal: fsck error in packed object
Поэтому я сделал git fsck:
error in tree c05ac7f76dcd3e8fb3b7faf7aab9b7a855647867: duplicateEntries: contains duplicate file entries
error in tree 0d7286cedf43c65e1ce9f69b74baaf0ca2b73e2b: duplicateEntries: contains duplicate file entries
error in tree 7f14e6474400417d11dfd5eba89b8370c67aad3a: duplicateEntries: contains duplicate file entries
Следующее, что я сделал, это проверил git ls-tree c05ac7f76dcd3e8fb3b7faf7aab9b7a855647867
:
100644 blob c233c88b192acfc20548d9d9f0c81c48c6a05a66 fileA.cs
100644 blob 5d6096cb75d27780cdf6da8a3b4d357515f004e0 fileB.cs
100644 blob 5d6096cb75d27780cdf6da8a3b4d357515f004e0 fileB.cs
100644 blob d2a4248bcda39c0dc3827b495f7751b7cc06c816 fileC.xaml
Обратите внимание, что fileB.cs
отображается дважды с одним и тем же хэшем. Я предполагаю, что это проблема, потому что почему файл два раза находится в одном и том же дереве с одним и тем же именем файла и хэшем большого двоичного объекта?
Теперь я погуглил проблему, но не смог найти способ, как это исправить. Я нашел один, казалось бы, хороший ресурс: Дерево содержит повторяющиеся записи файлов.
Тем не менее, в основном это сводится к использованию git replace, который на самом деле не решает проблему, поэтому git fsck все равно будет печатать ошибку и не позволит мне нажать на удаленный компьютер.
Затем есть этот, который, кажется, полностью удаляет файл (но мне все еще нужен файл, но только один раз, а не дважды в дереве): https://stackoverflow.com/a/44672692/826244
Есть ли другой способ исправить это? Я имею в виду, что это действительно должно быть возможно исправить, чтобы git fsck не выдавал никаких ошибок, верно? Я знаю, что мне нужно будет переписать всю историю после поврежденных коммитов. Я даже не мог найти способ получить фиксацию, указывающую на определенные деревья, иначе я мог бы использовать перебазирование и исправление поврежденной фиксации или что-то в этом роде. Любая помощь будет принята с благодарностью!
ОБНОВИТЬ: Почти уверен, что я знаю какие, но пока не знаю как, чтобы это сделать:
git mktree
<- готовоgit filter-branch -- --all
<- Должны сохраняться замены коммитовК сожалению, я не могу просто использовать git replace --edit
на плохом дереве, а затем запустить git filter-branch -- --all
, потому что filter-branch
, похоже, работает только с коммитами, но игнорирует замены дерева...
git версии 2.21.0 для Windows, gitlab 11.9 для Linux, не уверен, какой именно. Но проблема воспроизводится в Windows и Linux, проверка с помощью git clone --mirror
, затем запуск git fsck
Так что сам репозиторий, видимо, поврежден.
Да, и я хочу это исправить, если это возможно
Я не вижу в вашем ответе git show <bad_tree>
, упомянутого в stackoverflow.com/a/24868719/6309: это по крайней мере показало бы повторяющуюся запись.
git show c05ac7f76dcd3e8fb3b7faf7aab9b7a855647867
будет просто печатать то же самое, что и git ls-tree c05ac7f76dcd3e8fb3b7faf7aab9b7a855647867
, но только имена файлов, а не идентификатор BLOB-объекта и тип
OK. fileB.cs
дублируется тогда. Тогда не уверен, учитывая, что я написал stackoverflow.com/a/44672692/826244: может быть, применить его в любом случае, сохранив каждый созданный коммит, где файл удаляется, а затем снова отфильтровать его, чтобы добавить его обратно в каждый из этих коммитов...
Итак, если я правильно понимаю, мне придется полностью удалить файлы из репозитория, а затем снова добавить их в соответствующие ветки, добавив коммит, верно? Проблема в том, что мне пришлось бы сохранять текущее состояние файла во всех наших тегах релиза и, скорее всего, проект не сможет собраться для всех коммитов, где файл отсутствует. Пока это правильно? Если возможно, я бы хотел, чтобы репозиторий не создавался для большинства коммитов за последние полгода...
Идея заключалась бы в том, чтобы сначала удалить, а затем добавить обратно файл на втором этапе, и все это было бы сделано в локальном репозитории для тестирования: никто другой не будет подвергаться воздействию «неполного» (не компилируемого) репозитория. Задача состоит в том, чтобы сохранить состояние одного из двух fileB.cs
, а также новый коммит, созданный их удалением. Таким образом, второй шаг будет изменять их для каждого из этих новых коммитов, добавляя обратно (один раз) соответствующий fileB.cs
, в результате чего получится дерево только с одним fileB.cs
на затронутый коммит.
Хорошо, тогда я удаляю файлы (пока не должно быть проблем с bfg или git filter-branch), но как мне добавить файлы на втором этапе? Поскольку все последующие коммиты должны будут измениться, мне придется работать с веткой фильтра, верно? Так? stackoverflow.com/a/54200033/826244 Будет ли тогда cp выполняться только для одного коммита или для всех коммитов, начиная с начальной точки? Другая проблема заключается в том, что рассматриваемые файлы очень часто менялись за последние 6 месяцев, поэтому было бы предпочтительнее другое решение.
Согласен, то, что я имею в виду, не так просто реализовать: двухэтапный процесс git filter-branch, один для удаления, один для добавления (и да, добавляйте в каждую фиксацию, добавляя правильный контент для этого файла, сохраненный во время первый шаг)
Каким было окончательное использованное решение для этой проблемы?
Я опубликую обновление позже, но в основном я распаковал файлы пакета, а затем написал инструмент для исправления дефектных деревьев, их коммитов и всех последующих коммитов. Загружу инструмент на github через несколько дней, чтобы любой мог легко его исправить.
Вы можете удалить связанные ссылки и истечь срок действия их объектов.
Чтобы найти связанные ссылки, запустите:
$ git log --all --format=raw --raw -t --no-abbrev
и найдите изменение ша, затем найдите его в $ git show-refs
Затем для каждой ссылки, содержащей плохие объекты, выполните:
$ git update-ref -d refs/changes/xx/xxxxxx/x
Наконец, истечет срок действия объектов и запустите fsck, это должно быть исправлено.
$ git reflog expire --expire=now --all
$ git gc --prune=now --aggressive
$ git fsck
Мне все еще нужны все ссылки и их объекты. Мне просто нужно изменить объекты, чтобы они больше не были недействительными.
@ Тим, ты пытался вместо этого использовать команду rebase? git перебазировать -fr (git-scm.com/docs/git-rebase)
Идея состоит в том, чтобы создать ветку из предыдущей фиксации, а затем перебазировать ее из исходной ветки с параметрами -fr.
Вы можете попробовать запустить git fast-export
, чтобы экспортировать ваш репозиторий в файл данных, а затем запустить git fast-import
, чтобы повторно импортировать файл данных в новый репозиторий. Git удалит все повторяющиеся записи во время процесса быстрого импорта, что решит вашу проблему.
Имейте в виду, что вам, возможно, придется принять решение о том, как обрабатывать подписанные теги и тому подобное при экспорте, передав соответствующие аргументы в git fast-export
; так как вы переписываете историю, вы, вероятно, хотите пройти --signed-tags=strip
.
В настоящее время я пытаюсь это сделать. Однако к настоящему времени он работает около 4 часов, а целевой репозиторий становится огромным, поэтому мне нужно будет потом запустить перепаковку. Я обновлю, если он работает, когда он закончится.
Он переписал все коммиты, репозиторий стал слишком большим, даже после репака, и это заняло слишком много времени:/
Запуск импорта кажется вечным.
Это медленный процесс.
Я обнаружил проблему, связанную с тем, что gitlab не имеет fsck.skipList
, и я думаю, что решение может применяться:
Чтобы перейти к новому проекту в gitlab, парень использовал функцию импорта при создании этого проекта GitLab и импортировал его прямо из другого своего репозитория.
Примечание. Это не исправило это локально, но позволило импортировать его, и, возможно, импорт таким образом создал чистую ветку удаленно.
Окончательным решением было написать инструмент, решающий эту проблему.
Первым шагом было git unpack-objects все пак-файлы.
Затем мне пришлось идентифицировать коммиты, которые указывали на записи дерева с дубликатами, прочитав все ссылки, а затем вернувшись в историю, проверив все деревья.
После того, как у меня были инструменты для этого, было не так сложно теперь переписать деревья этих коммитов, а затем переписать все коммиты после этого. После этого мне пришлось обновить измененные ссылки. Это момент, когда я тщательно проверил результат, так как еще ничего не было потеряно.
Наконец, git reflog expire --expire=now --all && git gc --prune=now --aggressive
переписал пакет и удалил все незакрепленные объекты, которые больше не доступны.
Когда у меня будет время, я загружу исходный код на github, так как он работает очень хорошо и может быть шаблоном для подобных проблем. Он работал всего несколько минут в репозитории объемом 3,7 ГБ (около 20 ГБ в распакованном виде). К настоящему времени я также реализовал чтение из пакфайлов, так что больше не нужно ничего распаковывать (что занимает много времени и места).
Обновление: я немного поработал над исходным кодом, и теперь он работает очень хорошо, даже лучше, чем bfg для удаления одного файла (пока нет переключателей). Исходный код доступен здесь: https://github.com/TimHeinrich/GitRewrite Имейте в виду, это было протестировано только с одним репозиторием и только под Windows на ядре i7. Маловероятно, что он будет работать на Linux или на любой другой архитектуре процессора.
Проголосовали, но не забудьте обновить этот ответ ссылкой на ваш инструмент после его публикации на GitHub. Или у нас будет другой случай «маржи»: объяснитеxkcd.com/wiki/index.php/1381:_Маржа
Добавил ссылку на исходники
Отлично спасибо! Должна быть возможность кросс-компилировать ваш инструмент на другую платформу.
Да, я просто не уверен, правильно ли я проверил порядок байтов для других платформ, а также везде ли я использовал независимые от платформы разделители путей.
Какая версия ОС и Git у вас есть на вашей стороне (клиент) и на стороне GitLab (сервер, если только самостоятельный хостинг также не означает самостоятельный хостинг на том же компьютере)?