Как исправить поврежденный репозиторий git?
Я привязал клонирование своего репо, которое я храню в своей папке Ubuntu, к новой машине, и я получил это:
christopher@christopher-laptop:~/source/personal$ git clone ~/Ubuntu\ One\ Side\ Work/projects.git/
Cloning into 'projects'...
done.
fatal: unable to read tree 29a422c19251aeaeb907175e9b3219a9bed6c616
christopher@christopher-laptop:~/source/personal$
Поэтому я попытался посмотреть на многие другие вопросы, подобные этому, которые были заданы здесь, и большинство из них говорят, что нужно бежать git fsck --full
и тогда я получаю это, когда пытаюсь это сделать.
christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (447/447), done.
broken link from commit 235ae1f48701d577d71ebd430344a159e5ba4881
to commit 984c11abfc9c2839b386f29c574d9e03383fa589
broken link from tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
to blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
broken link from tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
to blob dd4e97e22e159a585b20e21028f964827d5afa4e
broken link from tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
to tree 29a422c19251aeaeb907175e9b3219a9bed6c616
broken link from tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
to tree 8084e8e04d510cc28321f30a9646477cc50c235c
broken link from tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
to blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
broken link from tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
to blob e9052d35bfb6d30065b206fc43f4200a04d5281b
broken link from tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
to blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
broken link from tree 4aa336dc1a5838e8918e03b85580069d83f4ad09
to tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
broken link from tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
to blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
broken link from tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
to blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
broken link from tree 252ab84542264e1589576b6ee51e7a31e580a0e2
to tree 2069041cd5950e529e2991d37b7290ec021d90d4
broken link from tree 2d4964aa4d4f5d8c7228518ce72ef6a63f820c6d
to blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
broken link from tree c7192e82fc581bd6448bda1a25e8729bdac5f4ff
to blob 30d54d47ae82add1917ca173d42e58b396df580b
broken link from tree 7c66306901fc71389623286936cef172d4ffe408
to blob bc7e05d705401273b1df4e939de0f540597c0931
broken link from tree 0940f5fd227d4c84d6e6749d872db50a4522ae3a
to tree 923767594ac22023e824948d65622fe5b407d1a1
broken link from tree 8eadcd2a971e8357d24f0d80f993d2963452209f
to blob 2598bde3dc8cb80ee49510b8159344004b88645f
broken link from tree ffa302dd0d969172ef23caeefe856ab2f57a4e4d
to blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
broken link from tree 7045b8870a49ce30a2027537a96d73d162bda773
to blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
broken link from tree 37e4705d34bd440ce681ae32ae9a180a13256d72
to tree 246f564d4cee53339b8a4244f3173b61caa518eb
missing blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
missing blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
missing tree 29a422c19251aeaeb907175e9b3219a9bed6c616
missing tree 8084e8e04d510cc28321f30a9646477cc50c235c
missing blob 30d54d47ae82add1917ca173d42e58b396df580b
missing tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
missing blob e9052d35bfb6d30065b206fc43f4200a04d5281b
dangling tree 4b26e95db542c72ac4a22ec25abe38fb2de79752
missing blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
missing blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
missing tree 923767594ac22023e824948d65622fe5b407d1a1
missing blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
missing blob 2598bde3dc8cb80ee49510b8159344004b88645f
dangling tree 3a683869f1bb0c1634de75700c316b3b36570dbd
dangling blob 4098d30843380d798a811f1aa9a02994f0dbbb27
missing tree 2069041cd5950e529e2991d37b7290ec021d90d4
missing blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
missing blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
missing blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
dangling tree 6c7b5162aa7a303fa3fe8dc393c5da564e309521
missing commit 984c11abfc9c2839b386f29c574d9e03383fa589
missing blob bc7e05d705401273b1df4e939de0f540597c0931
missing blob dd4e97e22e159a585b20e21028f964827d5afa4e
missing tree 246f564d4cee53339b8a4244f3173b61caa518eb
dangling commit a01f5c1e5315dc837203d6dee00d3493be9c5db9
Это выглядит очень плохо. Когда я делаю git log | head
Я понял
christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git log | head
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881
commit 2fb0d2d0643b445440f01b164f11ee9ee71fca48
Author: christopher <christopher@christopher.christopher>
Date: Wed Aug 7 15:51:42 2013 -0400
finishing chapter 7
Другие вопросы здесь сказали, чтобы посмотреть на ./git/refs/heads/master
, Это голый репо и refs/heads/
существует, но refs/heads/master
не. ГОЛОВА в голом репо говорит ref: refs/heads/master
хоть
packed-refs
говорит это хотя
# pack-refs with: peeled
2fb0d2d0643b445440f01b164f11ee9ee71fca48 refs/heads/master
Еще другие вопросы предложили запустить git reflog
и никакой вывод не появляется, когда я запускаю это.
Так что я действительно понятия не имею, что здесь делать. Какую стратегию следует принять? Можно ли сбросить голову на этот последний коммит 7 августа?
РЕДАКТИРОВАТЬ:
Выполнение журнала git и переход к нижней части экрана выводит следующее:
commit 996e03b949aea176238e3c7a8452700bbb987ac9
Author: christopher <christopher@christopher>
Date: Wed Jul 3 23:00:44 2013 -0400
many many changes
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881
Это, кажется, мешает работе git prune
18 ответов
В качестве альтернативы последней опции CodeGnome, если поврежден только локальный репозиторий, и вы знаете URL-адрес удаленного, вы можете использовать это для переустановки .git
для согласования с пультом (замена ${url}
с удаленным URL):
mv -v .git .git_old && # remove old git
git init && # initialise new repo
git remote add origin "${url}" && # link to old repo
git fetch && # get old history
git reset origin/master --mixed # force update to old history
Это оставляет ваше рабочее дерево нетронутым и влияет только на бухгалтерию git.
Я также недавно сделал сценарий bash для этой цели (Приложение A), который обернул немного безопасности вокруг этой операции.
Замечания:
Если в вашем репо есть подмодули, этот процесс каким-то образом испортит их, и единственное решение, которое я нашел до сих пор, - это их удаление, а затем использование git submodule update --init
(или повторное клонирование репо, но это кажется слишком резким).
Приложение А - Полный сценарий
#!/bin/bash
# Author: Zoey Llewellyn "Zobean" Hewll
#
# Usage: fix-git [REMOTE-URL]
# Must be run from the root directory of the repository.
# If a remote is not supplied, it will be read from .git/config
#
# For when you have a corrupted local repo, but a trusted remote.
# This script replaces all your history with that of the remote.
# If there is a .git, it is backed up as .git_old, removing the last backup.
# This does not affect your working tree.
#
# This does not currently work with submodules!
# This will abort if a suspected submodule is found.
# You will have to delete them first
# and re-clone them after (with `git submodule update --init`)
#
# Error codes:
# 1: If a url is not supplied, and one cannot be read from .git/config
# 4: If the url cannot be reached
# 5: If a git submodule is detected
if [[ "$(find -name .git -not -path ./.git | wc -l)" -gt 0 ]] ;
then
echo "It looks like this repo uses submodules" >&2
echo "You will need to remove them before this script can safely execute" >&2
echo "Then use \`git submodule update --init\` to re-clone them" >&2
exit 5
fi
if [[ $# -ge 1 ]] ;
then
url="$1"
else
if ! url="$(git config --local --get remote.origin.url)" ;
then
echo "Unable to find remote 'origin': missing in '.git/config'" >&2
exit 1
fi
fi
url_base="$(echo "${url}" | sed -E 's;^([^/]*://)?([^/]*)(/.*)?$;\2;')"
echo "Attempting to access ${url_base} before continuing"
if ! wget -p "${url_base}" -O /dev/null -q --dns-timeout=5 --connect-timeout=5 ;
then
echo "Unable to reach ${url_base}: Aborting before any damage is done" >&2
exit 4
fi
echo
echo "This operation will replace the local repo with the remote at:"
echo "${url}"
echo
echo "This will completely rewrite history,"
echo "but will leave your working tree intact"
echo -n "Are you sure? (y/N): "
read confirm
if ! [ -t 0 ] ; # i'm open in a pipe
then
# print the piped input
echo "${confirm}"
fi
if echo "${confirm}"|grep -Eq "[Yy]+[EeSs]*" ; # it looks like a yes
then
if [[ -e .git ]] ;
then
# remove old backup
rm -vrf .git_old | tail -n 1 &&
# backup .git iff it exists
mv -v .git .git_old
fi &&
git init &&
git remote add origin "${url}" &&
git config --local --get remote.origin.url | sed 's/^/Added remote origin at /' &&
git fetch &&
git reset origin/master --mixed
else
echo "Aborting without doing anything"
fi
TL; DR
Git не хранит историю так, как вы думаете. Он вычисляет историю во время выполнения на основе цепочки предков. Если у вашего предка нет блобов, деревьев или коммитов, вы не сможете полностью восстановить свою историю.
Восстановить недостающие объекты из резервных копий
Первое, что вы можете попробовать, это восстановить недостающие элементы из резервной копии. Например, посмотрите, есть ли у вас резервная копия фиксации, сохраненная как .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589
, Если это так, вы можете восстановить его.
Вы также можете просмотреть git-verify-pack и git-unpack-objects в случае, если коммит уже был упакован, и вы хотите вернуть его свободному объекту в целях операции репозитория.
Хирургическая Резекция
Если вы не можете заменить отсутствующие элементы из резервной копии, возможно, вы сможете удалить недостающую историю. Например, вы можете проверить свою историю или reflog, чтобы найти предка коммита 984c11abfc9c2839b386f29c574d9e03383fa589. Если вы найдете один нетронутым, то:
- Скопируйте ваш рабочий каталог Git во временный каталог.
- Сделайте полный сброс до неиспользованного коммита.
- Скопируйте ваши текущие файлы обратно в рабочее дерево Git, но убедитесь, что вы не копируете папку.git обратно!
- Фиксируйте текущее рабочее дерево и сделайте все возможное, чтобы рассматривать его как сжатый коммит всей отсутствующей истории.
Если это сработает, вы, конечно, потеряете промежуточную историю. На этом этапе, если у вас есть журнал рабочей истории, то хорошей идеей будет обрезать свою историю и перекомпилировать все недостижимые коммиты и объекты.
Полное восстановление и повторная инициализация
Если ваш репозиторий все еще поврежден, то, надеюсь, у вас есть не поврежденная резервная копия или клон, из которого вы можете восстановить. Если нет, но ваш текущий рабочий каталог содержит допустимые файлы, то вы всегда можете повторно инициализировать Git. Например:
rm -rf .git
git init
git add .
git commit -m 'Re-initialize repository without old history.'
Это радикально, но это может быть единственным вариантом, если история вашего хранилища действительно не поддается восстановлению. YMMV.
В последнее время у меня возникли аналогичные проблемы с использованием git версии 2.7.1 под Ubuntu 18.04.3. Вот как я это сделал:
sudo apt install git-repair
git-repair # fix a broken git repository
or
git-repair --force # force repair, even if data is lost
git fsck # to verify it was fixed
В большинстве случаев процесс восстановления был успешным
Прежде чем попробовать какие-либо исправления, описанные на этой странице, я бы посоветовал сделать копию вашего репозитория и работать только с этой копией. Затем, в конце концов, если вы можете это исправить, сравните его с оригиналом, чтобы убедиться, что вы не потеряли ни одного файла в процессе восстановления.
Другой вариант, который мне помог, состоял в том, чтобы вернуть git head и index в предыдущее состояние, используя:
git reset --keep
Вы также можете сделать то же самое вручную, открыв графический интерфейс Git и выбрав каждое "поэтапное изменение" и нажав "Отменить этап изменения". Когда все неустановлено, теперь вы сможете сжать базу данных, проверить базу данных и зафиксировать.
Я также попробовал следующие команды, но они не работали для меня, но они могли бы для вас зависеть от конкретной проблемы:
git reset --mixed
git fsck --full
git gc --auto
git prune --expire now
git reflog --all
Наконец, чтобы избежать этой проблемы синхронизации, повреждающей ваш git-индекс (что может случиться с DropBox, SpiderOak или любым другим облачным диском), вы можете сделать следующее:
- Конвертировать ваши
.git
папку в один "git-файл" bundle, используя:git bundle create my_repo.git --all
тогда он должен работать точно так же, как и раньше, но, поскольку все находится в одном файле, вы больше не рискуете синхронизацией, которая повредит вашему git-репо. - Отключить мгновенную синхронизацию: SpiderOak позволяет установить расписание проверки изменений на "автоматическое" (что означает, что он как можно скорее отслеживает изменения файлов благодаря уведомлениям ОС). Это плохо, потому что он начнет загружать изменения, как только вы вносите изменения, а затем загружать изменения, чтобы он мог стереть последние изменения, которые вы только что делали. Решением этой проблемы является установка задержки мониторинга изменений на 5 минут и более. Это также устраняет проблемы с мгновенным сохранением заметок приложений (таких как Notepad++).
Если у вас настроен пульт, и вы не беспокоитесь о потере некоторого не выдвинутого кода, вы можете сделать:
git fetch && git reset --hard
Я попытался удалить объектные файлы с 0 байтами и извлечь их снова с пульта, и это сработало:
find . -type f -size 0 -exec mv {} /tmp \;
git fetch
Он извлек недостающие объекты с пульта и позволил мне продолжить работу без повторной инициализации всего репо.
Вот скрипт (bash) для автоматизации первого решения @CodeGnome для восстановления из резервной копии (запускается с верхнего уровня поврежденного хранилища). Резервное копирование не должно быть завершено, оно должно иметь только отсутствующие объекты.
git fsck 2>&1 | grep -e missing -e invalid | awk '{print $NF}' | sort -u |
while read entry; do
mkdir -p .git/objects/${entry:0:2}
cp ${BACKUP}/objects/${entry:0:2}/${entry:2} .git/objects/${entry:0:2}/${entry:2}
done
git-ремонт (
sudo apt install git-repair
) с несколькими дополнительными командами, которые мне помогли:
Создайте резервную копию поврежденного репозитория.
Удалить неработающие ссылки:
find .git/refs -size 0 -delete -print
Восстановить репозиторий с удаленного(-ых):
git-repair --force
Очистите оборванные коммиты:
git gc --prune=now
Получить последнее состояние с удаленного компьютера:
git fetch
При желании переключитесь на master и сбросьте его на origin/master:
git checkout master
git reset --hard origin/master
Проверьте с помощью git fsck:
git fsck
Если вы в отчаянии, вы можете попробовать это:
git clone ssh://me@my.git.server/path/to/project destination --depth=1
Он получит ваши данные, но вы потеряете историю. Я пошел методом проб и ошибок в моем репо и --depth=10
работал, но --depth=50
дал мне неудачу.
Я столкнулся с той же проблемой, поэтому я заменил папку.git резервной версией, и она все еще не работала, потому что файл.gitconfig был поврежден. BSOD на моем ноутбуке испортил его. Я заменил его следующим кодом, а sourcetree восстановил все мои репозитории.
[user]
name = *your username*
email = *your email address*
[core]
autocrlf = true
excludesfile = C:\\Users\\*user name*\\Documents\\gitignore_global.txt
Я не знаю, поможет ли это кому-нибудь, но это просто еще одно решение, которое сработало для меня.
Я создал новый клон репозитория в другой папке, импортировал из него блобы.
В исходном репозитории (сначала сделайте резервную копию) я запустил:
git reset --hard origin/main # DESTRUCTIVE - reset index to a known good state
git unpack-objects < /path/to/new/clone/.git/objects/pack/<hash>.pack
git fsck --full | grep -v dangling
После этого все было ок :)
Я хотел добавить это в качестве комментария под удивительным ответом Зои Хьюил выше, но у меня в настоящее время нет достаточно представителей, чтобы сделать это, поэтому я должен добавить это здесь и отдать должное ее работе:P
Если вы используете Poshgit и чувствуете себя чрезвычайно ленивым, вы можете использовать следующее, чтобы автоматически извлечь ваш URL из вашей конфигурации git и упростить работу. Стандартные предостережения относятся к проверке этого на копии / резервное копирование вашего локального репо в случае, если он взорвется вам в лицо.
$config = get-content .git\config
$url = $config -match " url = (?<content>.*)"
$url = $url.trim().Substring(6)
$url
move-item -v .git .git_old;
git init;
git remote add origin "$url";
git fetch;
git reset origin/master --mixed
В моем случае я создавал репозиторий из исходного кода уже на моем компьютере, и эта ошибка появилась. Я удалил папку.git и сделал все снова, и это сработало:)
Хотя мой другой ответ здесь сработал для меня, можно попробовать еще один вариант:
- Создайте новый клон пульта
- Добавьте новый клон в качестве альтернативы git.
- Бегать
git fsck
снова со всеми объектами, присутствующими через альтернативный
Быстрый способ, если у вас есть изменения в вашем текущем проекте и вы не хотите его потерять, перенесите ваш текущий проект куда-нибудь, клонируйте проект из github в эту папку и внесите некоторые изменения, чтобы попытаться зафиксировать снова. Или просто удалите репо и клонируйте его снова, это сработало у меня.
Если ничего из вышеперечисленного не работает, вот отчет о том, что изменение git с отключенными проверками сработало для меня очень хорошо.Я только что зафиксировал им "безвозвратное" репо.
Сущность:
Бинарные объекты git (внутри
Для этого требуется немного навыков C, по крайней мере, на том уровне, на котором вы можете исправить и перекомпилировать git из исходного кода. Что вам нужно изменить, это всегда одно и то же: git где-то прекращает обработку с фатальной ошибкой, поэтому вы закомментируете это и замените пустым макетом. После этого
Это может быть причиной того, что разработка git не реализует столь незначительное, но чрезвычайно полезное улучшение. Думаю, требуемый размер патча, скорее всего, будет меньше 100 строк.
Дальнейшее развитие идеи, возможно, также может быть создана вилка git, которая может восстанавливать такие поврежденные репозитории.
diff -urNw orig/sha1-file.c patched/sha1-file.c
--- orig/sha1-file.c 2021-03-08 21:36:01.000000000 +0100
+++ patched/sha1-file.c 2021-08-19 18:32:44.019115545 +0200
@@ -1285,8 +1285,16 @@
else if (stream->avail_in)
error(_("garbage at end of loose object '%s'"),
oid_to_hex(oid));
+
+ error("error ignored");
+
+ /*
free(buf);
return NULL;
+ */
+
+ status = Z_OK;
+ return buf;
}
/*
@@ -1656,7 +1664,7 @@
oid_to_hex(repl), oid_to_hex(oid));
if (!stat_loose_object(r, repl, &st, &path))
- die(_("loose object %s (stored in %s) is corrupt"),
+ error(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(repl), path);
if ((p = has_packed_and_bad(r, repl->hash)) != NULL)
@@ -1664,7 +1672,9 @@
oid_to_hex(repl), p->pack_name);
obj_read_unlock();
- return NULL;
+ errno = 0;
+ error("error ignored");
+ return strdup("");
}
void *read_object_with_reference(struct repository *r,
@@ -2473,10 +2483,12 @@
}
git_inflate_end(stream);
+ /*
if (status != Z_STREAM_END) {
error(_("corrupt loose object '%s'"), oid_to_hex(expected_oid));
return -1;
- }
+ }*/
+
if (stream->avail_in) {
error(_("garbage at end of loose object '%s'"),
oid_to_hex(expected_oid));