如何修复损坏的git存储库?


104

我试图将我的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不存在。HEAD裸回购说ref: refs/heads/master,虽然

packed-refs 确实这样说

# pack-refs with: peeled 
2fb0d2d0643b445440f01b164f11ee9ee71fca48 refs/heads/master

还有其他问题建议运行,git reflog而在我运行时没有任何输出。

所以我真的不知道该怎么办。应该采取什么策略?是否可以将头重置为8月7日的最后一次提交

编辑:

进行git log并转到屏幕输出的底部,将显示以下内容:

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的工作


3
这并没有太大帮助,但是:不要将Git存储库存储在Dropbox或任何其他同步服务中。Git并不是用来处理另一个程序在做其他事情时随机锁定和重写文件的。
millimoose 2013年


2
几乎所有答案都假定一个人可以简单地从一些不腐败的远程起源重新克隆。这是问题所在...如果您原籍并且被损坏了怎么办?对。因此,这git-repair是一个程序,它将运行git fsck并尝试解决其遇到的任何问题。git-repair.branchable.com似乎功能很强大,尽管您最终可能不得不从备份中复制(如果可以!)对象(您有备份,对吗?),这样做应该可以节省大量时间尽一切可能挽救您的工作,而不是进行大量自动化任务。没有隶属关系等
underscore_d

Answers:


110

作为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(或重新克隆该回购,但这似乎太过激烈了)。

附录A-完整脚本

#!/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

3
壮观的是,我备份了我的项目并尝试了您的解决方案。Git莫名其妙地被损坏了。我尝试了您的解决方案,效果很好,非常感谢。
kequc '17

2
不像我在Windows上那样使用脚本,但是这些命令只是保存了.git文件夹,该文件夹显示了对任何跟踪文件进行的每次保存都会显示新的存储库(因此在尝试此修复程序之前,我最终得到了50个主存储库)
助力车

1
完成此操作后,我能够还原我的.git文件夹。我发现git reset origin/master --hard比有用--mixed
费利佩·阿尔瓦雷斯

1
大。如果有子更改不包含更改,请先将其删除,然后再执行子模块初始化以再次获取它们。
herm

1
@ w33haa不幸的是,此解决方案仅适用于拥有有效且可访问的远程存储库的情况。其他一些答案解决了无法访问或损坏的遥控器的情况。
Zoey Hewll '19

53

TL; DR

Git并没有真正按照您的想法存储历史记录。它根据祖先链在运行时计算历史记录。如果您的祖先缺少斑点,树木或提交,那么您可能无法完全恢复历史记录。

从备份还原丢失的对象

您可以尝试的第一件事是从备份中还原丢失的项目。例如,请查看是否有保存为的提交的备份.git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589。如果是这样,您可以还原它。

如果提交已经被打包,并且为了存储库手术的目的将其返回到一个松散的对象,您可能还想研究git-verify-packgit-unpack-objects

手术切除

如果您无法从备份中替换丢失的项目,则可以删除丢失的历史记录。例如,您可以检查历史记录或reflog来查找984c11abfc9c2839b386f29c574d9e03383fa589的祖先。如果发现一个完好无损,则:

  1. 将您的Git工作目录复制到某个地方的临时目录中。
  2. 对未损坏的提交进行硬重置。
  3. 将当前文件复制回Git工作树,但请确保不要将.git文件夹复制回去!
  4. 提交当前的工作树,并尽力将其视为所有丢失的历史记录的压缩提交。

如果有效,您当然会失去介入的历史。在这一点上,如果您有可用的历史记录日志,那么最好修剪一下历史记录并重新引用所有无法到达的提交和对象。

完全还原和重新初始化

如果您的存储库仍然损坏,那么希望您的备份或克隆没有损坏,可以从中还原。如果不是,但是您当前的工作目录包含有效文件,那么您随时可以重新初始化Git。例如:

rm -rf .git
git init
git add .
git commit -m 'Re-initialize repository without old history.'

这太剧烈了,但是如果您的存储库历史记录确实不可恢复,那么这可能是您唯一的选择。YMMV。


3
我只是遇到了这个问题,并想指出这一点,因为它不在您的答案中,但是我的问题只是一个简单的权限问题。error: Could not read abcde我的仓库由GitLab管理,它正在创建用户无法读取的文件。一个sudo chown后来和我是好去。
2016年

6

如果您配置了远程服务器,并且/不关心丢失一些未推送的代码,则可以执行以下操作:

git fetch && git reset --hard

2
在某些情况下,如果某些对象已损坏,git不会让您获取,因此您需要先重新初始化您的存储库。
Zoey Hewll

当我这么做的时候这对我没用fatal: pack has 13 unresolved deltas
Artem Russakovskii

5

这是一个脚本(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

4

在尝试本页上描述的任何修补程序之前,我建议您制作一份您的仓库,并仅对此副本进行操作。然后,如果可以修复,最后将其与原始文件进行比较,以确保在修复过程中不会丢失任何文件。

对我有用的另一种方法是使用以下命令将git head和index重置为其以前的状态:

git reset --keep

您也可以通过打开Git GUI并选择每个“分段的更改”,然后单击“取消分段”来手动执行相同的操作。一切就绪后,您现在应该可以压缩数据库,检查数据库并提交。

我还尝试了以下命令,但它们对我不起作用,但它们可能对您有用,具体取决于您遇到的问题:

git reset --mixed
git fsck --full
git gc --auto
git prune --expire now
git reflog --all

最后,为避免此同步问题损坏您的git索引(DropBox,SpiderOak或任何其他云磁盘可能会发生这种情况),您可以执行以下操作:

  1. .git通过使用:文件夹转换为单个“捆绑” git文件git bundle create my_repo.git --all,则它应该与以前一样工作,但是由于所有内容都在单个文件中,因此您再也不必担心同步会损坏git repo。
  2. 禁用瞬时同步:SpiderOak允许您将检查更改的时间表设置为“自动”(这意味着它可以尽快通过OS通知来监视文件更改)。这很不好,因为它会在您进行更改后立即开始上传更改,然后下载更改,因此它可能会擦除您刚才所做的最新更改。解决此问题的解决方案是将更改监视延迟设置为5分钟或更长时间。这也解决了即时保存笔记应用程序(例如Notepad ++)的问题。

3

如果您不顾一切,可以尝试以下操作:

git clone ssh://me@my.git.server/path/to/project destination --depth=1

它将获取您的数据,但您将丢失历史记录。我在回购中反复尝试并--depth=10工作,但--depth=50失败了。


3

我尝试将目标文件移走0个字节,然后再次从远程获取它们,并且它的工作原理是:

find . -type f -size 0 -exec mv {} /tmp \;
git fetch

它从远程获取了丢失的对象,并允许我继续工作而无需重新初始化整个存储库。


2

我遇到了同样的问题,因此我用备份版本替换了“ .git”文件夹,但由于.gitconfig文件已损坏,它仍然无法正常工作。我笔记本电脑上的BSOD损坏了它。我用以下代码替换了它,sourcetree恢复了我所有的存储库。

[user]
name = *your username*
email = *your email address*
[core]
autocrlf = true
excludesfile = C:\\Users\\*user name*\\Documents\\gitignore_global.txt

我不知道这是否对任何人都有帮助,但这只是对我有用的另一种解决方案。



2

最近在Ubuntu 18.04.3下使用git版本2.7.1遇到了类似的问题。这是我的做法:

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-repair确实是一个非常有用的工具。它帮助我恢复了存储库。大多数其他方法的问题是您丢失了藏匿处,这对我来说不是一个选择。
Jan Rychter

1

就我而言,我是从PC中已经存在的源代码创建存储库的,并且出现了该错误。我删除了.git文件夹,并再次执行了所有操作,它:)


1

我想在上面Zoey Hewil的出色回答下添加此评论作为评论,但是我目前没有足够的代表这样做,因此我必须在此处添加并赞扬她的工作:P

如果您正在使用Poshgit并感到异常懒惰,则可以使用以下内容从git配置中自动提取URL,并使轻松工作变得更加容易。标准警告适用于在副本上测试此内容/首先备份本地存储库,以防它在您的脸上爆炸。

$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

0

如果您对当前项目进行了更改并且不想丢失它,请快速将其移动到某个位置,将项目从github克隆到此文件夹,然后进行一些更改以尝试再次提交。或者只是删除该存储库并再次克隆它,它对我有效。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.