141

我尝试将保存在Ubuntu One文件夹中的存储库克隆到新机器上,结果如下:

cd ~/source/personal
git clone ~/Ubuntu\ One\ Side\ Work/projects.git/

Cloning into 'projects'...
done.
fatal: unable to read tree 29a422c19251aeaeb907175e9b3219a9bed6c616

因此,我尝试查看此处已提出的许多其他类似问题,其中大多数都说要运行git fsck --full,然后当我尝试时我得到了这个。

cd ~/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我得到了这个

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,当我运行它时没有输出显示。

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

执行 agit 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 修剪工作。

4

14 回答 14

176

作为 Todd 的最后一个选项(完全恢复和重新初始化)的替代方案,如果只有本地存储库损坏,并且您知道远程的 URL,您可以使用它来重置您.git以匹配远程(替换${url}为远程 URL ):

mv -v .git .git_old &&            # Remove old Git files
git init &&                       # Initialise new repository
git remote add origin "${url}" && # Link to old repository
git fetch &&                      # Get old history
# Note that some repositories use 'master' in place of 'main'. Change the following line if your remote uses 'master'.
git reset origin/main --mixed     # Force update to old history.

这会使您的工作树完好无损,并且只会影响 Git 的簿记。

我最近也为此目的制作了一个 Bash 脚本(附录 A),它为这个操作提供了一些安全性。

笔记:

  • 如果您的存储库有子模块,此过程会以某种方式将它们弄乱,到目前为止我发现的唯一解决方案是删除它们然后使用git submodule update --init(或重新克隆存储库,但这似乎激烈了)。
  • 这会尝试根据本地配置设置确定“主”和“主”之间的正确选择,但是如果在使用“主”的存储库上使用“主”作为默认分支的机器上可能会出现一些问题.
  • 这用于wget在执行任何操作之前检查 url 是否可访问。这不一定是确定站点可访问的最佳操作,如果您没有 wget 可用,则可能会替换为ping -c 1 "${url_base}"(linux)、ping -n 1 "${url_base}"(windows) 或curl -Is "${url_base}"

附录 A - 完整脚本

也作为gist发布,尽管它现在已经过时了。

#!/bin/bash

# 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

if ! branch_default="$(git config --get init.defaultBranch)" ;
then
    # if the defaultBranch config option isn't present, then it's likely an old version of git that uses "master" by default
    branch_default="master"
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/${branch_default}" --mixed
else
    echo "Aborting without doing anything"
fi
于 2017-10-06T11:18:29.797 回答
58

TL;博士

Git 并没有像你想象的那样真正存储历史。它基于祖先链在运行时计算历史。如果您的祖先缺少 blob、树或提交,那么您可能无法完全恢复您的历史记录。

从备份中恢复丢失的对象

您可以尝试的第一件事是从备份中恢复丢失的项目。例如,查看您是否有存储为.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。

于 2013-09-08T18:31:32.913 回答
9

在尝试此页面上描述的任何修复之前,我建议制作您的存储库的副本并仅处理此副本。然后在最后,如果您可以修复它,请将其与原始文件进行比较,以确保您在修复过程中没有丢失任何文件。

另一个对我有用的替代方法是将 Git 头和索引重置为之前的状态:

git reset --keep

您也可以通过打开 Git GUI 并选择每个“暂存更改”并单击“取消暂存更改”来手动执行相同操作。当一切都未暂存时,您现在应该能够压缩数据库、检查数据库并提交。

我也尝试了以下命令,但它们对我不起作用。但它们可能适合您,具体取决于您遇到的确切问题:

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

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

  1. .git使用:将您的文件夹转换为单个“捆绑”Git 文件git bundle create my_repo.git --all,那么它应该和以前一样工作,但是由于所有内容都在一个文件中,您将不会再冒同步损坏您的 git 存储库的风险。
  2. 禁用瞬时同步:SpiderOak 允许您将检查更改的调度设置为“自动”(这意味着它会尽快,通过操作系统通知监视文件更改)。这很糟糕,因为它会在您进行更改后立即开始上传更改,然后下载更改,因此它可能会删除您刚刚所做的最新更改。解决此问题的解决方案是将更改监视延迟设置为 5 分钟或更长时间。这也解决了即时保存笔记应用程序(例如Notepad++)的问题。
于 2017-07-07T16:05:18.423 回答
6

我最近在Ubuntu 18.04.3 (Bionic Beaver)下使用 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

大多数情况下,恢复过程是成功的。

于 2019-12-18T07:20:55.470 回答
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
于 2017-05-09T20:57:34.323 回答
5

如果您配置了遥控器并且您/不关心丢失一些未推送的代码,您可以这样做:

git fetch && git reset --hard
于 2017-06-02T16:34:56.543 回答
4

我尝试移开 0 字节的目标文件并再次从远程获取它们,它起作用了:

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

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

于 2019-02-28T16:14:13.193 回答
3

如果你很绝望,你可以试试这个:

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

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

于 2015-03-27T14:24:51.700 回答
1

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

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

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

于 2015-06-10T16:23:38.250 回答
1

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

于 2017-02-13T14:45:50.750 回答
0

我想在上面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
于 2019-07-02T11:34:33.700 回答
0

如果以上都不起作用,这里有一份报告,用禁用检查修改 git 对我来说非常有效。我只是用它修复了一个“不可恢复的”回购。

精华:

git 二进制对象(内部.git/objects)只是 gzip 压缩的 blob,以二进制数据格式打包在一起。在大多数情况下,如果这些文件被损坏,您可以获得非常好的部分恢复 - 如果 git 支持它。它有各种检查(主要是:无法打开目标文件,或解压缩 blob 结果错误)它停止处理并出现致命错误。

它需要一点 C 技能,至少在你可以从源代码修补和重新编译 git 的水平上。您需要修改的内容总是相同的:git 某处停止处理并出现致命错误,因此您将其注释掉并用空的模拟替换它。之后,一个/your/patched/git gc --aggressive --prune=now(常见的超级激进的重新打包程序)修复了它应该修复的内容。例如,下面的补丁可以修复一些目标文件损坏的 git repo。当然,恢复只是部分的:受损对象中的文件丢失,甚至是追溯丢失。

这可能就是为什么 git 开发没有实现如此微小但非常有用的改进的原因。我认为,所需的补丁大小可能低于 100 行。

进一步发展这个想法,可能还可以创建一个 git fork,它可以恢复这些损坏的 repos。

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));
于 2021-08-19T18:07:39.250 回答
0

如果您对当前项目有更改并且不想丢失它,那么一种快速方法是将当前项目移动到某处,将项目从 GitHub 克隆到此文件夹并进行一些更改并尝试再次提交。

或者只是删除存储库并再次克隆它。它对我有用。

于 2019-05-06T13:59:01.010 回答
-3

这个命令对我有用:

git reset --mixed
于 2018-06-19T12:27:10.370 回答