当您“遇到麻烦”时:版本控制的故事

谢谢您嘲笑我的搞笑头衔。 但是你知道什么不好笑吗? 当您将提交推送到git存储库时,您会在GitHub Desktop中看到以下内容:

当您“遇到麻烦”时:版本控制的故事
尝试使用Google找出这意味着什么

是的,我知道很酷的人使用Git Tower ,而真正的很酷的人仅使用命令行 我们真的很酷,所以我们将使用命令行来解决此问题。 实际上,我们别无选择-这就是您加入本文的冒险之旅:修复一个git repo,尽管您本身 没有命令行git专业知识 ,但由于您自己没有任何过错 ,突然突然损坏。 但是至少您能看到我的恐慌。

第一步是诊断问题。 但是,如果您像我一样,您也会遇到一个meta问题 ,这是因为您依赖于您几乎不了解的工具,这使诊断变得困难。 寻找有同情心的专家来帮助您解决自己的具体情况也是一个挑战。 您甚至可能不知道该问谁或应该问什么问题。 不过,如果您有耐心,系统且愿意学习 ,您可以了解事情的运作方式以及如何逐步解决自己的困境。 朋友们,这对我来说是一个git参考文档的旅行,在那里我发现了git-fsck命令,我有义务在我的repo的根目录中运行了该命令,并产生了以下(截断的)输出:

> git fsck

...

error: object file .git/objects/67/99ddac675cab54060cdfb066dbfadb6708fc3f is empty
error: object file .git/objects/67/99ddac675cab54060cdfb066dbfadb6708fc3f is empty
fatal: loose object 6799ddac675cab54060cdfb066dbfadb6708fc3f (stored in .git/objects/67/99ddac675cab54060cdfb066dbfadb6708fc3f) is corrupt

因此,这里的资源,以及如果您的一个项目因眨眼而倒下的情况, 可能损坏的存储库 哦。 很高兴知道。 怎么办? 当你想知道生命的意义时,你问上帝 在这种情况下,我查阅了来自Linus Torvalds的一封古老电子邮件 ,该电子邮件恰好解决了类似情况。

一个Git回购确实是一个图形各种的二进制对象 :斑点,树木和提交。 Blob对象是加密散列的数据Blob,每个Blob代表一个文件。 这些blob对象彼此独立,但是它们也由所谓的树对象链接,这些树对象将blob有效地分组为类似于文件系统目录结构的排列。 最后,还有提交对象,其中包含跟踪树和Blob中的更改所必需的信息。 提交对象也按顺序链接(如您所料)。

Git根据提交ID的前几个字符将所有这些对象存储在位于.git/objects/中的一系列嵌套目录中(如您在上面看到的)。 例如,对象6799ddac675cab54060cdfb066dbfadb6708fc3f存储在名为67/的目录中,作为文件99ddac675cab54060cdfb066dbfadb6708fc3f 也就是说,完整的对象名称是其存储目录和该目录中特定文件的组合。

因此,如果其中一个提交对象损坏,则整个回购可能会变成无用的字节堆 ,因为链接的提交链将被破坏。 那是坏消息。 好消息是,由于您的回购是不连续文件的集合,因此,即使其中一个对象损坏了无法修复,您也可以将其恢复到健康状态—如果您可以执行足够精确的手术

那就是我试图做的。

按照Linus的建议,我将损坏的提交目标文件./git/objects/67/99ddac675cab54060cdfb066dbfadb6708fc3f移到了其他位置。 您可以将损坏的物体停在任何您喜欢的地方 无论如何,它们可能最终会变成垃圾。

我碰巧收到blob对象67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf的相同错误消息,所以我也移动了该错误消息。 然后我再次尝试了文件系统检查:

> git fsck

Checking object directories: 100% (256/256), done.
Checking objects: 100% (8970/8970), done.
broken link from tree 03a88f876eb3f6157f76461a3ae6cb18bbb86561
to blob 67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf
dangling commit 76814e15074b540bc2f7e78daf3f5175a8759523
missing commit 6799ddac675cab54060cdfb066dbfadb6708fc3f
missing blob 67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf
dangling blob 2a60520000698ad964e4e61fab31f9b862763550
dangling commit 41634cd81964068acb153bfa355d63bd80fc7cef
dangling commit 5bf415e2bdbc47822ae99b64c2a0f6b4f288eefb

请注意,Linus建议使用git fsck --full ,但这是现在的默认行为。

忽略“悬挂提交”消息,“断开链接”消息告诉我哪个树对象指向我刚刚删除的Blob对象。 实际上,我故意断开了链接以显示此信息。 树对象03a88f876eb3f6157f76461a3ae6cb18bbb86561期望指向斑点67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf ,但斑点不存在。 提交对象6799ddac675cab54060cdfb066dbfadb6708fc3f (我移动的另一个对象)也报告丢失。 到目前为止,一切都很好。

继续Linus的建议,我现在有足够的信息来使用git-ls-tree命令列出上面提到的树对象的内容:

> git ls-tree 03a88f876eb3f6157f76461a3ae6cb18bbb86561

100644 blob 312d8994f1005a9563a9410c592b27000c201101 building-test.js
100644 blob f84006fd14c6d4b2ccc3ef22b2fe02abf535bd1a folds-test.js
100644 blob 67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf index.js
100644 blob 8b1f47bce7ec989dff7e936279d63d1d02f6a92d indexing-test.js
100644 blob 3f2b45f8cd9dfd486c8e821ee672ed66a34768df inf-test.js
100644 blob 0e6d1985aa59d17e2115bc6c7936d2ac88b00457 list-test.js
100644 blob 40310b5df53691d0e1ba4118c0e3ab66ed766990 reducing-test.js
100644 blob 8244d5fb2768ad5c7c33890ee26c797c2df6262b searching-test.js
100644 blob ff34a2f15faf6eec7d9c9635e79d2a0abdadfb42 sub-test.js
100644 blob ac0c029cc64870c7445c4bdd9d7fe20646b5cc33 trans-test.js
100644 blob 99781d303e90b7aa4de8d630c1053a42f87e8331 zip-test.js

扫描列表,我发现了罪魁祸首67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf及其关联文件: index.js 因此,现在我知道了问题的根源,但是我不知道文件的哪个版本导致了问题的开始。 返回命令行:

> git log --raw --all

commit cf63a71497e027d96614cfff6ba1d297f1a1a26e
Author: Steven Syrek <[email protected]>
Date: Mon Jul 18 11:55:40 2016 -0400

Add tests for set operations on lists

:100644 100644 67a45ac... c1c2f99... M test/list/index.js
:000000 100644 0000000... 23c47fe... A test/list/set-test.js

commit f3bc2c55b22deb889f99cdd45663c20a8e8e79c1
Author: Steven Syrek <[email protected]>
Date: Mon Jul 18 11:14:13 2016 -0400

Add tests for list zipping and unzipping functions and remove exponentiation operator from tests and examples

:100644 100644 01af47b... 3b1bf35... M source/list/zip.js
:100644 100644 21206e2... 67a45ac... M test/list/index.js
:000000 100644 0000000... 99781d3... A test/list/zip-test.js

带有--raw--all选项的git-log命令将显示存储库的整个提交历史记录。 我只显示我上面的相关部分。 我们在这里看到的是对象21206e20386e0365bc6f15d0ccd372b1c72b5667在损坏的对象21206e20386e0365bc6f15d0ccd372b1c72b5667之前,而对象67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf之后继之后的提交(它们以相反的顺序列出),紧随其后的是对象c1c2f99072ef41aca89e963cfb0143f897e0de78

此时,Linus说我完成了,因为我发现了损坏的提交之前和之后的文件版本:

如果可以这样做,现在可以使用git hash-object -w <recreated-file>重新创建丢失的对象,并且存储库再次正常!

不幸的是,在尝试了此操作之后,我的存储库又不好了。 现在事情开始变得多毛了 过去的Linus失去了建议,现在的Linus(现在也过去的Linus)可能要做的事比帮助我更好 因此,我不得不遵循专业开发人员每天使用的精巧故障排除流程:

谷歌。 谷歌。

堆栈溢出。 天哪,它下来了

这种千篇一律的方法使我尝试了一些事情,从git-diff命令开始。 如果我无法自动重新创建丢失的对象,我会拼命地推理,也许我可以手动完成:

> git diff 206e20386e0365bc6f15d0ccd372b1c72b5667..c2f99072ef41aca89e963cfb0143f897e0de78

fatal: ambiguous argument '206e20386e0365bc6f15d0ccd372b1c72b5667..c2f99072ef41aca89e963cfb0143f897e0de78': unknown revision or path not in the working tree.

哎呀。 我忘记了主角:

> git diff 21206e20386e0365bc6f15d0ccd372b1c72b5667..c1c2f99072ef41aca89e963cfb0143f897e0de78

diff --git a/21206e20386e0365bc6f15d0ccd372b1c72b5667..c1c2f99072ef41aca89e963cfb0143f897e0de78 b/c1c2f99072ef41aca89e963cfb0143f897e0de78
index 21206e2..c1c2f99 100644
--- a/21206e20386e0365bc6f15d0ccd372b1c72b5667..c1c2f99072ef41aca89e963cfb0143f897e0de78
+++ b/c1c2f99072ef41aca89e963cfb0143f897e0de78
@@ -25,3 +25,7 @@ export * from './sub-test';
export * from './searching-test';

export * from './indexing-test';
+
+export * from './zip-test';
+
+export * from './set-test';

上面是index.js中在损坏的提交两侧的两次提交之间更改的行。 它们用+标记,周围还显示了一些周围的线。 删除的行(如果有的话)将以-标记。 由于两个相同的文件在进行哈希处理时应产生相​​同的哈希键,因此我认为我会尝试通过删除更改的行并手动重新创建提交来强制解决方案:

> git hash-object -w ./test/list/index.js

2a60520000698ad964e4e61fab31f9b862763550

不。 再试一次,也许只是删除标记为+的行。

> git hash-object -w ./test/list/index.js

21206e20386e0365bc6f15d0ccd372b1c72b5667

不,但是很有趣。 我设法在损坏的提交发生之前重新创建了对象的原始状态,但是我想我真正想做的是重新创建正确的中间状态? 幸运的是,因为我一直都处于良好的git卫生阶段,所以没有太多的可能性。 因此,我再次更改了文件,仅在我回忆起一切之前就添加了那些标有+行:

git hash-object -w ./test/list/index.js

67a45ac2f58a444fa4db11cd9ab7e024a8e35dcf

好极了。

> git fsck

Checking object directories: 100% (256/256), done.
Checking objects: 100% (8970/8970), done.
dangling commit 76814e15074b540bc2f7e78daf3f5175a8759523
missing commit 6799ddac675cab54060cdfb066dbfadb6708fc3f
dangling blob 2a60520000698ad964e4e61fab31f9b862763550
dangling commit 41634cd81964068acb153bfa355d63bd80fc7cef
dangling commit 5bf415e2bdbc47822ae99b64c2a0f6b4f288eefb

哦,对了,我现在有一个健康的Blob,但是我仍然缺少指向它的提交对象。 怎么办? 救援是git-gc

> git gc

error: Could not read 6799ddac675cab54060cdfb066dbfadb6708fc3f
error: Could not read 6799ddac675cab54060cdfb066dbfadb6708fc3f
warning: reflog of 'HEAD' references pruned commits
warning: reflog of 'refs/heads/restructure' references pruned commits
error: Could not read 6799ddac675cab54060cdfb066dbfadb6708fc3f
fatal: Failed to traverse parents of commit b267a6a8264c0cdc72d047049610fc91e9f7c06f
error: failed to run repack

或不。 那本来是垃圾收集所有…垃圾。 并修复…所有问题。 我不知道为什么会这样。 但是我希望。 我真的,真的希望。 然后我被禁re 注意到上面的“ reflog”消息,我想到了下一个绝妙的主意:

> git reflog expire --all --stale-fix

error: Could not read 6799ddac675cab54060cdfb066dbfadb6708fc3f
fatal: Failed to traverse parents of commit b267a6a8264c0cdc72d047049610fc91e9f7c06f

众所周知,使用命令行工具时 ,添加的选项越多,则男性化就越多。 如果您不知道他们的工作没关系 真正的男人不阅读man :他们只是快速移动并破坏事物 另外,我宁愿重新整理我的仓库。 但不是。 那也不起作用。

到现在为止,我已经完全沉浸在尝试任何事情的过程中,而又不考虑理智健全性 我转回原木,这总是让人感到沮丧,承认失败并找到一个可以悄悄哭泣的角落。 但是,也许一个解决方案会奇迹般地呈现出来,这是我以前错过的事情,但是有没有所有人可以看到的?

> git log 6799ddac675cab54060cdfb066dbfadb6708fc3f

fatal: bad object 6799ddac675cab54060cdfb066dbfadb6708fc3f

不。

> git ls-tree 6799ddac675cab54060cdfb066dbfadb6708fc3f

fatal: not a tree object

不。 我的意思是 不知何故,我有了一个聪明的主意,那就是只检查存储库的restructure分支的日志,这是致命的闪烁光标进入我的生活时我一直在进行的工作:

> tail -n 40 .git/logs/refs/heads/restructure

...

44dc22e706fb029a9c96f3bd125755fd55ac882b 6799ddac675cab54060cdfb066dbfadb6708fc3f Steven Syrek <[email protected]> 1468788351 -0400 commit: Replace isEq function in all tests with should.eql
6799ddac675cab54060cdfb066dbfadb6708fc3f b267a6a8264c0cdc72d047049610fc91e9f7c06f Steven Syrek <[email protected]> 1468789759 -0400 commit: Separate out functions in Ord tests

...

我想:“也许我对blob对象所做的相同的比较会在提交对象上起作用,” 所以:

> git diff b267a6a8264c0cdc72d047049610fc91e9f7c06f..44dc22e706fb029a9c96f3bd125755fd55ac882b

...

(bunch of irrelevant stuff)

好。 不。但是至少我还有一个提交哈希44dc22e706fb029a9c96f3bd125755fd55ac882b ,可以用来做一些事情 这是我的6799ddac675cab54060cdfb066dbfadb6708fc3f之前的最后一个好人, 6799ddac675cab54060cdfb066dbfadb6708fc3f我的世界6799ddac675cab54060cdfb066dbfadb6708fc3f黑暗。 我查阅了文档。 我查阅了互联网。 我在黑暗中又拍了一张照片:

> git branch -l rewrite-tests 44dc22e706fb029a9c96f3bd125755fd55ac882b

我在这里所做的是创建一个名为rewrite-tests的新分支,并根据git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]使用44dc22e706fb029a9c96f3bd125755fd55ac882b提交(即最后一个好的提交)作为起点git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] git-branch docs中指定的git branch [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]模式。 我实际上不确定-l选项的用途,甚至是否有必要。 有人说要使用它。 耸耸肩。

然后,我将所有文件移出了仓库,并进行了以下操作之一:

git checkout rewrite-tests

git-checkout命令将HEAD设置为指定的分支。 换句话说,我告诉git我想在rewrite-tests分支上工作。 然后,我只是将所有文件复制回去,重新提交,然后离开restructure分支以致枯竭。

就这样,令我惊讶的是,我完成了。 最坏的情况已经过去,而且还没有太快:我开始在哈希键中看到。 我有一个新的分支要开发,但我的工作丝毫没有丢失(尽管有一些中间提交不合时宜地死亡)。 最终,我将所有内容都压回了master ,尽管现在我倾向于避免在任何回购中直接在该分支上工作,以防这些kerfuffle之一再次出现。

我仍然不时拜访Blinky女士 ,只是为了幸灾乐祸。 实际上,不,我不这样做。 但是,如果愿意,您可以自己访问我的受伤和维修过的存储库:它包含我的maryamyriameliamurphies.js项目。 我完全独自处理大量的代码。 您可以想象当我以为自己可能毁了它时的感受。 以及当我想出解决方法时的感受。

在本文开始时,我建议损坏的git存储库(由于它是由离散的对象组成),可以通过仔细的手术来恢复。 我们已经看到了这种操作的两种可能性。 在损坏的斑点对象的情况下,首先是切除有问题的斑点,然后通过重新整理原始文件在伤口上缝合。 第二个,如果第一个失败(或者问题是一个损坏的提交对象,而不仅仅是一个blob),则在损坏的分支处将受伤的分支切断,将新分支移植到树桩上,并重新提交所有是程序的伤亡。

这些是不同的解决方案,但相似之处在于,它们都需要在相当低的级别上修复数据结构,即使它只是操作文件也是如此。 实际上, 由于 git repo存储为一系列文件,因此修复操作是完全可能的。 就文件系统而言,它实际上只是具有接口,命令行的大型数据结构,而git repo也是具有接口,git命令及其各种子命令和选项的类似于文件系统的数据结构。 如果您可以从命令行学习如何使用文件系统,换句话说,您也可以学习如何使用git。

我希望这个故事能解决一系列特定的说明,以解决您自己一天可能遇到的问题。 不幸的是,我只能提供一些鼓励:您可以做到! 因为很难知道这些错误的原因,更不用说修复它们的最佳方法了,所以也很难一概而论。 您所能做的就是深入挖掘并反击熵。 学习您的工具,不要害怕。 如果您只是为了体验,请自己尝试一下,然后再恳求一位科学家朋友来帮助您。 只记得先备份!

如果这里有明显的道义,那么当您面对可怕的,闪烁的状态栏或它的晦涩的命令行等效项时,它使恢复变得非常容易得多,而且花费也少得多:提早提交,并经常提交。 毕竟, 预防保健通常是最好的药物。

From: https://hackernoon.com/when-you-git-in-trouble-a-version-control-story-97e6421b5c0e