如何在 'git push --set-upstream origin master' 之后恢复代码
How to recover code after 'git push --set-upstream origin master'
我试图将我的代码推送到一个新的存储库,但以典型的 git 方式,如果您不了解每个细节,学习和内爆代码就像火箭手术一样容易。好的,我做了一些事情,现在我的大部分代码都不见了,那么我该如何取消删除我的代码或取回它呢?
那么如何撤销 bash git 上的错误?
为什么没有“git 撤消”?
我(不小心)输入的是“git push --set-upstream origin master”,现在我的代码被清除了。好吧,我希望它仍然在一个文件夹中,但由于任何 'because git' 原因而没有显示。
我已经尝试了三年来理解 git 但它对我来说仍然是 90% 的胡言乱语。
'git reflog' 是 google 搜索显示的内容,但它只显示了这个信息。
f668709 (HEAD, origin/master) HEAD@{0}: rebase: checkout origin/master
5061c5e (master) HEAD@{1}: commit (initial): first new commit
Why isn't there a "git undo" ?
有……嗯,有点。这里有很多收获。
What I typed in ( by accident ) was "git push --set-upstream origin master"
这不会对您自己存储库中的任何提交进行任何更改。这也是一种“安全”的推送,因此它不会破坏 other Git 存储库中的任何内容,在 origin
上。应该没有任何 到 撤消。
如果推送失败——我怀疑它失败了——你可能会被 Git 鼓励到 运行 git pull
或 git pull --rebase
或类似的。 不过,这可能是个错误。
I've tried for three years to understand git and it still 90% gibberish to me.
您可能需要不同的方法。 :-) 你熟悉图论吗?如果是,或者即使不是,请考虑仔细阅读网站 Think Like (a) Git。但这是在更基本的事情之后完成的:意识到 Git 经常 隐藏 什么 Git 有,并向你展示完全是别的东西。
'git reflog' is what google search is showing but it just shows me this info.
f668709 (HEAD, origin/master) HEAD@{0}: rebase: checkout origin/master
5061c5e (master) HEAD@{1}: commit (initial): first new commit
这强烈表明(尽管本身并不能证明):
- 您的 Git 存储库中可能只有两个提交(至少有两个);和
- 不是
git push origin master
引起了麻烦,而是 git rebase
。
如果您运行 git status
,它可能会告诉您您正处于变基过程中。在 git push origin master
.
之后,你必须有 运行 东西
如果您 处于变基过程中,您可以终止该过程并将事情恢复到 git rebase --abort
时的状态。但是请注意,如果您已经完成了一些您想要保存的工作,此 --abort
操作可能会将其丢弃:首先复制您想要保存的任何文件!
长:你的文件去了哪里
我会在此处写一篇 long-ish(对于 我 来说不会那么长)描述你的文件去了哪里,以及它们在 [= =25=],假设他们真的回来了。我认为,根据你在问题中所说的,他们会的。您可以跳到下一部分,但最好通读整个内容。
你理解 Git
的第一个关键点是什么
如果您要使用 Git(当然,没有人说您 有 ),花一些时间了解它在做什么可能是明智的,以及为什么它如此顽固。原因是 Git 并不是真正关于 文件 .
当您工作时,您可能会处理文件。毕竟,这就是您的计算机存储的内容:组织到目录/文件夹中的文件。所以你会期望 Git 与这些一起工作。它的确如此——有点。但它坚持这样做它的方式。
Git 所做的是存储 提交 。这些是编号的实体。您的 git reflog
输出显示了两个提交:
5061c5e
:这是你自己在本地制作的;
f668709
:某人——可能不是你——制作了这个,可能在其他Git存储库中。
这两个提交可能相互关联也可能不相关。要找出答案,您可以 运行 git log --all --decorate --graph
。相互关联的提交连接在一起,如下所示:
* commit 72c4083ddf91b489b7b7b812df67ee8842177d98 (HEAD -> master, origin/master, origin/HEAD)
| Author: Junio C Hamano <gitster pobox.com>
| Date: Wed Jan 6 23:22:15 2021 -0800
|
| The first batch in 2.31 cycle
|
| Signed-off-by: Junio C Hamano <gitster pobox.com>
|
* commit d3aff11c3eadf3c496859180d453ce07cde72b44
|\ Merge: cf4b0714f7 5bc12c11cc
| | Author: Junio C Hamano <gitster pobox.com>
(这种 git log
通常显示完整的提交编号,而不是 human-friendly1 您在 git reflog
输出。)
这表明提交 2c4083ddf...
是早期 parent 的 child提交 d3aff11c3...
。 (parent 提交本身是一些更老祖先的 child,它们是更老祖先的 children,依此类推。)
最后,提交是 编号的 ,这些丑陋的大字符串 letters-and-digits,并且可以有某种家庭关系。这一切都很好,但仍然没有告诉你它们是什么,或者它们有什么好处。
的提交分为两部分。 任何提交的所有部分都是 read-only: 任何提交中的任何内容都不能更改。您 可以 从 Git 中提交,处理/使用它,然后放回修改后的版本,但您得到的是 new commit,使用新编号。具有旧编号的旧提交保持不变。同时,这两个部分是:
提交的主体是 Git 知道的每个文件的已保存快照,其形式为 in Git 在你或任何人创造它的时候。这是我们将文件放入 Git 的地方:它们存在于提交中。但是您无法直接查看这些文件。
其中一个原因是它们的存储方式与您的计算机存储文件的方式不同。相反,它们被压缩和冻结,并且——这样你的 Git 存储库就不会很快变得非常庞大——de-duplicated。大多数提交主要是 re-use 他们以前提交的大部分文件。所以,代替d 将每个文件的新副本放入每个新提交中,Git ar运行 基础文件数据 共享 所有 次提交。
Git 让这个共享的确切机制在不同的地方展示出来。这可能是好事也可能是坏事,具体取决于您查看本应只是抽象的实现细节的喜好。为了避免这个答案变得太长,我不会在这里详细介绍;请记住,Git 往往对其细节过于自豪,时不时地用它们来打你的脸。我们稍后会在 Git 的 index.
中再次看到这一点
提交的其余部分——实际存储的部分作为提交;文件被间接存储以实现 de-duplication 技巧——包含元数据:关于提交本身的信息,例如提交人、时间和说明提交原因的日志消息。
提交的“家庭关系”存储在此元数据中。 Git 实现此目的的方法是存储此提交的 parent 的 提交编号 。其他一切都必须从这个 I am the child of ____ stuff stored in each commit.2
因此,提交允许您存储文件——事实上,它们要求您永久保存每个文件——并且还存储元数据,包括parent年龄。这就是 b运行ches 进来的地方。
1或者我应该说少一点human-hostile.
2您可能想知道为什么 parent 不记录他们的 children。这是因为提交的这个 read-only 方面。当我们进行新的提交时——child 中的一些 parent——child 知道它的 parent 是谁。但是当我们制作child时,我们还不知道itschildren会是什么。他们稍后将获得一个唯一的 random-looking hash-ID 号码。实际数字根本不是 运行dom:它们是提交内容的加密校验和。但是它们 是 不可预测的,因为,例如,提交包括那些 time-stamps。您必须知道 何时 您将进行 child 提交,以及其中的所有其他内容,以预测其哈希 ID。 (还有另一个问题,但仅此一项就够难了。)
B运行ches 只是一种记住提交编号的方法
当你第一次提交时——在你的例子中,5061c5e
(你给出了日志消息 first new commit
)——它是 stand-alone。它只是您使用 git add
告诉 Git 的所有文件的快照。它有一个丑陋的大哈希 ID,对于这个特定的提交是唯一的,永远不会再在任何 Git 存储库中的任何地方使用。这个在这里简称为5061c5e
,但让我们更短一点,称之为“commit A
”,然后画出来:
A
如果您要更改一些文件,git add
它们,然后再次 运行 git commit
,这将创建一个 new 快照每个文件——A
中的所有文件,根据你所做的任何更改进行更新,然后 git add
-ed——并从中进行新的提交。新提交的parent,我们简称为B
,将是现有提交A
,因此B
在其元数据中保存 A
的 hash-ID 号码 (5061c5e
)。
我们说提交 B
指向 提交 A
并像这样绘制:
A <-B
随着我们添加越来越多的提交,我们建立了一个简单的 backwards-looking 提交链:
A <-B <-C <-D
现在,这里的技巧是 Git 只能 找到 提交 按他们的 hash-ID 数字,在 Git 的最低水平。但数字很漂亮 human-hostile,即使是缩写。这就是为什么我一直在使用字母!如果我们不得不告诉 Git 提交的原始数量 D
只是为了使用它,那将是无法容忍的。因此,Git 将一组 名称 添加到 提交 和它保存的支持内容的集合中。这些名称如 master
:例如 b运行ch 名称。3 Git 选择让这些名称只存储一个哈希 ID。由于提交 D
是,在我们这里的插图中,last 提交 master
,这就是名称的 number将存储:
A <-B <-C <-D <--master
每当我们进行 new 提交时,Git:
- 使新提交指向当前提交:新提交
E
将指向D
;然后
- 将 new 提交编号写入 b运行ch name
所以如果我们进行新的提交 E
我们最终会得到:
A <-B <-C <-D <-E <--master
这就是我们增长一个b运行ch的方式,一次一个提交。我们使用 git checkout
或 git switch
到 select b运行ch name 从而 last comit 在 b运行ch 上,名字本身指向它。
像master
这样的b运行ch名称实际上是较长的“全名”的缩写:refs/heads/master
。这允许 Git 为 tag 名称使用相同的存储系统,这些名称在 refs/tags/
下,以及其他类型的名称。
你的文件去了哪里
让我们回过头来考虑一下这些提交。每个存储 所有 您的文件,但以一种奇特的 twisted-up、read-only、de-duplicated 方式存储。这些文件几乎无法使用。它们仅作为 存档 有用。必须先 提取 它们才能处理或使用它们。
然后,提取这些文件至少是许多 Git 命令的一部分。其中最明显的一个是 git checkout
。我们可以 git checkout
a b运行ch name,或者我们可以使用 not a b运行 ch 名称——包括任何历史提交的原始哈希 ID 号——到 git checkout
该历史提交。
为此,Git 需要一个可以从提交中填写 的区域。该区域也是您工作的地方。大多数时候,这个工作区是你的。但是,当您使用 git checkout
或其他 Git 命令明确 覆盖 您的工作区文件时,您告诉 Git:删除我现在这里的文件,并用其他文件替换它们。
一些 Git 命令在执行此操作时要小心,不要破坏任何 未提交的 文件。如果您告诉其他人这样做,他们会很高兴地破坏您所有未保存的文件。幸运的是,您在这里使用的命令几乎肯定是更安全的品种。4
无论如何,我们称您工作的这个区域为您的工作树或work-tree。在这里,您的文件是 个文件 。他们有他们普通的日常形式。您实际上可以 使用 它们(想象一下!)。
如果您使用git checkout
的(安全版本)选择一些commit来工作from,Git 已从提交的 中保存的文件中填写了您的 work-tree。 git rebase
命令在变基开始时执行此操作。你想要拥有的文件发生了什么,它们被安全地存储在other提交:提交5061c5e
中。您现在 使用 提交 f668709
.
运行 git rebase --abort
会告诉 Git 停止变基,然后返回提交 5061c5e
。这将首先清除所有未保存的工作(!——这里的假设是你的 --abort
标志意味着 是的,我开始了这个但我想永远放弃它 ),然后做一个git checkout
的(安全版本)您之前 是 的提交,通过 git checkout master
.
4如果 Git 有一个“安全”和“不安全”命令的列表可能会有所帮助。不幸的是,这个列表令人抓狂:
git checkout
是安全的,但它也不安全。这种特殊的疯狂导致了 Git 2.23 中新的 git switch
和 git restore
命令,让人们将他们的旧 git checkout
习惯分成两个单独的命令,一个安全的和一个不安全的。不过,我还没有对自己的手指进行重新编程,您可能使用的是旧的 Git,它只有 all-in-one git checkout
.
git reset
大多数情况下是不安全的,除非你告诉它是安全的。
不过,git rebase
几乎总是安全的。
由于您正处于变基过程中,我们可以假设这里是安全的。
这里还有一件事要知道
您可能会认为,使用这种设置 - 当前提交 包含 frozen-forever 文件,以及那些文件已被复制的工作树您可以处理它们——这意味着当您在 Git 存储库中工作时,您总是有 每个文件的两个版本 可用。的确如此——许多 other 版本控制系统就是这样工作的——但它对 Git.
来说还不够
相反,Git 添加每个文件的 第三个 版本。我喜欢将每个文件的第三个副本视为“位于”冻结的已提交副本(我通常将其放在左侧)和可用副本(我通常将其放在右侧)之间。这给了我们一个中间副本。
那个中间副本存在于Git不同的地方,索引,或者暂存区,或者——现在很少见了——缓存。为什么这东西有三个名字?可能是因为 index 没有任何意义, cache 太具体了。 暂存区 一词反映了您作为一个人可以并且将会使用它的方式。这可能是对它最好的形容词。不过,它也是最新的,是在 Git 流行几年后发明的。
当Git 首先做了一些提交的 git checkout
,它真正做的是:
选择 that commit 作为 current commit,通过附加特殊名称 HEAD
它以某种方式:您在 git reflog
输出中看到它,其中包括行:5
f668709 (HEAD, origin/master)
将提交保存的文件复制到Git的索引/staging-area;和
将保存的文件复制到您的 work-tree,以便您可以查看和处理它们。
复制到 Git 的索引和您的 work-tree,需要从 Git 的索引和您的 [=] 中删除 585=] 安全存储在某些 other 提交中的任何文件。
现在,我们从其他版本控制系统的例子中知道,索引/staging-area 不是必需的。这个索引 / staging-area 的要点可能是让 Git 运行得更快(它确实做到了),或者可能是它启用的特殊技巧(它确实启用了特殊技巧)。我不太清楚原点 是 是什么。关于它的事情是你不能忽视它。如果你尝试,Git 会时不时地伸出手来打你的脸:你好!你现在必须对我的索引大惊小怪了!所以有必要学会忍受它,即使你永远不会喜欢它,只要你会使用Git.
索引内容很多。特别是,如果您正在进行合并(或任何足以 merge-like 的事情)并且存在 合并冲突 ,Git 将 扩展 索引,这样它就不再只是一个暂存区。
不过大多数时候,描述 Git 索引的一个好方法是它是您 提议的下一次提交 。 Git 只需从 current 提交中填写此提议的 next 提交。然后,每次更新工作树中的某些文件时,您需要告诉 Git:将我更新的文件复制回您的索引,以便我对 有不同的建议next commit. 这就是 git add
所做的:它制作某个文件的索引副本,与该文件的工作树副本相匹配。
然后,当您 运行 git commit
、Git 可以而且确实会打包 在 索引中的任何文件——它们'以特殊的 Git-only 形式存储在索引中——这些是 Git 知道的文件。这就是快照的来源:当您或任何人 运行 git commit
.
时,它就是 Git 索引中的任何内容
5这是一个奇怪的例子,因为 Git 处于 Git 调用的 detached HEAD 模式.此模式用于实现 git rebase
,但它不是您通常使用的模式。通常 HEAD
会被 附加 。不过,恐怕我要跳过这里的大部分内容。
结论
作为要点列表:
Git 存储 提交 ,而不是文件。然后提交存储文件。这使得每个提交都可以说是一个 all-or-nothing 交易。
Git 发现 通过哈希 ID 提交:内部提交编号。
Git 使用 b运行ch names 找到 last (最最近)在一些提交链中提交。这样你通常不需要使用又大又丑的哈希 ID。
通常,Git 通过 添加新提交 来工作。新的提交应该指向现有的提交,以便它们添加。如果他们不指向后方,那么关于 b运行ch names 找到 last 提交的事情,以及 Git从那里向后工作,停止工作。
提交中的文件仅可用作存档(以及差异化,以及我们可以对 frozen-for-all-time 文件执行的所有其他操作)。要处理/使用文件,您必须提取 提交。
使用 git checkout
提取提交会填充 Git 的索引和您的 work-tree。您可以查看和处理/使用的文件在您的 work-tree 中,并且是 您的,而不是 Git 的.它们实际上 不在 存储库中:当您从复制出来的内容中更改它们时,Git 对此一无所知。
事实上,您的 work-tree 文件不在 Git 中——它们就在旁边——这就是为什么你必须 git add
; add
仅更新 提议的下一次提交 的事实是您必须 git commit
.
的原因
其他一切都建立在上面之上。
我试图将我的代码推送到一个新的存储库,但以典型的 git 方式,如果您不了解每个细节,学习和内爆代码就像火箭手术一样容易。好的,我做了一些事情,现在我的大部分代码都不见了,那么我该如何取消删除我的代码或取回它呢?
那么如何撤销 bash git 上的错误?
为什么没有“git 撤消”?
我(不小心)输入的是“git push --set-upstream origin master”,现在我的代码被清除了。好吧,我希望它仍然在一个文件夹中,但由于任何 'because git' 原因而没有显示。
我已经尝试了三年来理解 git 但它对我来说仍然是 90% 的胡言乱语。
'git reflog' 是 google 搜索显示的内容,但它只显示了这个信息。
f668709 (HEAD, origin/master) HEAD@{0}: rebase: checkout origin/master
5061c5e (master) HEAD@{1}: commit (initial): first new commit
Why isn't there a "git undo" ?
有……嗯,有点。这里有很多收获。
What I typed in ( by accident ) was "git push --set-upstream origin master"
这不会对您自己存储库中的任何提交进行任何更改。这也是一种“安全”的推送,因此它不会破坏 other Git 存储库中的任何内容,在 origin
上。应该没有任何 到 撤消。
如果推送失败——我怀疑它失败了——你可能会被 Git 鼓励到 运行 git pull
或 git pull --rebase
或类似的。 不过,这可能是个错误。
I've tried for three years to understand git and it still 90% gibberish to me.
您可能需要不同的方法。 :-) 你熟悉图论吗?如果是,或者即使不是,请考虑仔细阅读网站 Think Like (a) Git。但这是在更基本的事情之后完成的:意识到 Git 经常 隐藏 什么 Git 有,并向你展示完全是别的东西。
'git reflog' is what google search is showing but it just shows me this info.
f668709 (HEAD, origin/master) HEAD@{0}: rebase: checkout origin/master 5061c5e (master) HEAD@{1}: commit (initial): first new commit
这强烈表明(尽管本身并不能证明):
- 您的 Git 存储库中可能只有两个提交(至少有两个);和
- 不是
git push origin master
引起了麻烦,而是git rebase
。
如果您运行 git status
,它可能会告诉您您正处于变基过程中。在 git push origin master
.
如果您 处于变基过程中,您可以终止该过程并将事情恢复到 git rebase --abort
时的状态。但是请注意,如果您已经完成了一些您想要保存的工作,此 --abort
操作可能会将其丢弃:首先复制您想要保存的任何文件!
长:你的文件去了哪里
我会在此处写一篇 long-ish(对于 我 来说不会那么长)描述你的文件去了哪里,以及它们在 [= =25=],假设他们真的回来了。我认为,根据你在问题中所说的,他们会的。您可以跳到下一部分,但最好通读整个内容。
你理解 Git
的第一个关键点是什么如果您要使用 Git(当然,没有人说您 有 ),花一些时间了解它在做什么可能是明智的,以及为什么它如此顽固。原因是 Git 并不是真正关于 文件 .
当您工作时,您可能会处理文件。毕竟,这就是您的计算机存储的内容:组织到目录/文件夹中的文件。所以你会期望 Git 与这些一起工作。它的确如此——有点。但它坚持这样做它的方式。
Git 所做的是存储 提交 。这些是编号的实体。您的 git reflog
输出显示了两个提交:
5061c5e
:这是你自己在本地制作的;f668709
:某人——可能不是你——制作了这个,可能在其他Git存储库中。
这两个提交可能相互关联也可能不相关。要找出答案,您可以 运行 git log --all --decorate --graph
。相互关联的提交连接在一起,如下所示:
* commit 72c4083ddf91b489b7b7b812df67ee8842177d98 (HEAD -> master, origin/master, origin/HEAD)
| Author: Junio C Hamano <gitster pobox.com>
| Date: Wed Jan 6 23:22:15 2021 -0800
|
| The first batch in 2.31 cycle
|
| Signed-off-by: Junio C Hamano <gitster pobox.com>
|
* commit d3aff11c3eadf3c496859180d453ce07cde72b44
|\ Merge: cf4b0714f7 5bc12c11cc
| | Author: Junio C Hamano <gitster pobox.com>
(这种 git log
通常显示完整的提交编号,而不是 human-friendly1 您在 git reflog
输出。)
这表明提交 2c4083ddf...
是早期 parent 的 child提交 d3aff11c3...
。 (parent 提交本身是一些更老祖先的 child,它们是更老祖先的 children,依此类推。)
最后,提交是 编号的 ,这些丑陋的大字符串 letters-and-digits,并且可以有某种家庭关系。这一切都很好,但仍然没有告诉你它们是什么,或者它们有什么好处。
的提交分为两部分。 任何提交的所有部分都是 read-only: 任何提交中的任何内容都不能更改。您 可以 从 Git 中提交,处理/使用它,然后放回修改后的版本,但您得到的是 new commit,使用新编号。具有旧编号的旧提交保持不变。同时,这两个部分是:
提交的主体是 Git 知道的每个文件的已保存快照,其形式为 in Git 在你或任何人创造它的时候。这是我们将文件放入 Git 的地方:它们存在于提交中。但是您无法直接查看这些文件。
其中一个原因是它们的存储方式与您的计算机存储文件的方式不同。相反,它们被压缩和冻结,并且——这样你的 Git 存储库就不会很快变得非常庞大——de-duplicated。大多数提交主要是 re-use 他们以前提交的大部分文件。所以,代替d 将每个文件的新副本放入每个新提交中,Git ar运行 基础文件数据 共享 所有 次提交。
Git 让这个共享的确切机制在不同的地方展示出来。这可能是好事也可能是坏事,具体取决于您查看本应只是抽象的实现细节的喜好。为了避免这个答案变得太长,我不会在这里详细介绍;请记住,Git 往往对其细节过于自豪,时不时地用它们来打你的脸。我们稍后会在 Git 的 index.
中再次看到这一点提交的其余部分——实际存储的部分作为提交;文件被间接存储以实现 de-duplication 技巧——包含元数据:关于提交本身的信息,例如提交人、时间和说明提交原因的日志消息。
提交的“家庭关系”存储在此元数据中。 Git 实现此目的的方法是存储此提交的 parent 的 提交编号 。其他一切都必须从这个 I am the child of ____ stuff stored in each commit.2
因此,提交允许您存储文件——事实上,它们要求您永久保存每个文件——并且还存储元数据,包括parent年龄。这就是 b运行ches 进来的地方。
1或者我应该说少一点human-hostile.
2您可能想知道为什么 parent 不记录他们的 children。这是因为提交的这个 read-only 方面。当我们进行新的提交时——child 中的一些 parent——child 知道它的 parent 是谁。但是当我们制作child时,我们还不知道itschildren会是什么。他们稍后将获得一个唯一的 random-looking hash-ID 号码。实际数字根本不是 运行dom:它们是提交内容的加密校验和。但是它们 是 不可预测的,因为,例如,提交包括那些 time-stamps。您必须知道 何时 您将进行 child 提交,以及其中的所有其他内容,以预测其哈希 ID。 (还有另一个问题,但仅此一项就够难了。)
B运行ches 只是一种记住提交编号的方法
当你第一次提交时——在你的例子中,5061c5e
(你给出了日志消息 first new commit
)——它是 stand-alone。它只是您使用 git add
告诉 Git 的所有文件的快照。它有一个丑陋的大哈希 ID,对于这个特定的提交是唯一的,永远不会再在任何 Git 存储库中的任何地方使用。这个在这里简称为5061c5e
,但让我们更短一点,称之为“commit A
”,然后画出来:
A
如果您要更改一些文件,git add
它们,然后再次 运行 git commit
,这将创建一个 new 快照每个文件——A
中的所有文件,根据你所做的任何更改进行更新,然后 git add
-ed——并从中进行新的提交。新提交的parent,我们简称为B
,将是现有提交A
,因此B
在其元数据中保存 A
的 hash-ID 号码 (5061c5e
)。
我们说提交 B
指向 提交 A
并像这样绘制:
A <-B
随着我们添加越来越多的提交,我们建立了一个简单的 backwards-looking 提交链:
A <-B <-C <-D
现在,这里的技巧是 Git 只能 找到 提交 按他们的 hash-ID 数字,在 Git 的最低水平。但数字很漂亮 human-hostile,即使是缩写。这就是为什么我一直在使用字母!如果我们不得不告诉 Git 提交的原始数量 D
只是为了使用它,那将是无法容忍的。因此,Git 将一组 名称 添加到 提交 和它保存的支持内容的集合中。这些名称如 master
:例如 b运行ch 名称。3 Git 选择让这些名称只存储一个哈希 ID。由于提交 D
是,在我们这里的插图中,last 提交 master
,这就是名称的 number将存储:
A <-B <-C <-D <--master
每当我们进行 new 提交时,Git:
- 使新提交指向当前提交:新提交
E
将指向D
;然后 - 将 new 提交编号写入 b运行ch name
所以如果我们进行新的提交 E
我们最终会得到:
A <-B <-C <-D <-E <--master
这就是我们增长一个b运行ch的方式,一次一个提交。我们使用 git checkout
或 git switch
到 select b运行ch name 从而 last comit 在 b运行ch 上,名字本身指向它。
像master
这样的b运行ch名称实际上是较长的“全名”的缩写:refs/heads/master
。这允许 Git 为 tag 名称使用相同的存储系统,这些名称在 refs/tags/
下,以及其他类型的名称。
你的文件去了哪里
让我们回过头来考虑一下这些提交。每个存储 所有 您的文件,但以一种奇特的 twisted-up、read-only、de-duplicated 方式存储。这些文件几乎无法使用。它们仅作为 存档 有用。必须先 提取 它们才能处理或使用它们。
然后,提取这些文件至少是许多 Git 命令的一部分。其中最明显的一个是 git checkout
。我们可以 git checkout
a b运行ch name,或者我们可以使用 not a b运行 ch 名称——包括任何历史提交的原始哈希 ID 号——到 git checkout
该历史提交。
为此,Git 需要一个可以从提交中填写 的区域。该区域也是您工作的地方。大多数时候,这个工作区是你的。但是,当您使用 git checkout
或其他 Git 命令明确 覆盖 您的工作区文件时,您告诉 Git:删除我现在这里的文件,并用其他文件替换它们。
一些 Git 命令在执行此操作时要小心,不要破坏任何 未提交的 文件。如果您告诉其他人这样做,他们会很高兴地破坏您所有未保存的文件。幸运的是,您在这里使用的命令几乎肯定是更安全的品种。4
无论如何,我们称您工作的这个区域为您的工作树或work-tree。在这里,您的文件是 个文件 。他们有他们普通的日常形式。您实际上可以 使用 它们(想象一下!)。
如果您使用git checkout
的(安全版本)选择一些commit来工作from,Git 已从提交的 中保存的文件中填写了您的 work-tree。 git rebase
命令在变基开始时执行此操作。你想要拥有的文件发生了什么,它们被安全地存储在other提交:提交5061c5e
中。您现在 使用 提交 f668709
.
运行 git rebase --abort
会告诉 Git 停止变基,然后返回提交 5061c5e
。这将首先清除所有未保存的工作(!——这里的假设是你的 --abort
标志意味着 是的,我开始了这个但我想永远放弃它 ),然后做一个git checkout
的(安全版本)您之前 是 的提交,通过 git checkout master
.
4如果 Git 有一个“安全”和“不安全”命令的列表可能会有所帮助。不幸的是,这个列表令人抓狂:
git checkout
是安全的,但它也不安全。这种特殊的疯狂导致了 Git 2.23 中新的git switch
和git restore
命令,让人们将他们的旧git checkout
习惯分成两个单独的命令,一个安全的和一个不安全的。不过,我还没有对自己的手指进行重新编程,您可能使用的是旧的 Git,它只有 all-in-onegit checkout
.git reset
大多数情况下是不安全的,除非你告诉它是安全的。
不过,git rebase
几乎总是安全的。
由于您正处于变基过程中,我们可以假设这里是安全的。
这里还有一件事要知道
您可能会认为,使用这种设置 - 当前提交 包含 frozen-forever 文件,以及那些文件已被复制的工作树您可以处理它们——这意味着当您在 Git 存储库中工作时,您总是有 每个文件的两个版本 可用。的确如此——许多 other 版本控制系统就是这样工作的——但它对 Git.
来说还不够相反,Git 添加每个文件的 第三个 版本。我喜欢将每个文件的第三个副本视为“位于”冻结的已提交副本(我通常将其放在左侧)和可用副本(我通常将其放在右侧)之间。这给了我们一个中间副本。
那个中间副本存在于Git不同的地方,索引,或者暂存区,或者——现在很少见了——缓存。为什么这东西有三个名字?可能是因为 index 没有任何意义, cache 太具体了。 暂存区 一词反映了您作为一个人可以并且将会使用它的方式。这可能是对它最好的形容词。不过,它也是最新的,是在 Git 流行几年后发明的。
当Git 首先做了一些提交的 git checkout
,它真正做的是:
选择 that commit 作为 current commit,通过附加特殊名称
HEAD
它以某种方式:您在git reflog
输出中看到它,其中包括行:5f668709 (HEAD, origin/master)
将提交保存的文件复制到Git的索引/staging-area;和
将保存的文件复制到您的 work-tree,以便您可以查看和处理它们。
复制到 Git 的索引和您的 work-tree,需要从 Git 的索引和您的 [=] 中删除 585=] 安全存储在某些 other 提交中的任何文件。
现在,我们从其他版本控制系统的例子中知道,索引/staging-area 不是必需的。这个索引 / staging-area 的要点可能是让 Git 运行得更快(它确实做到了),或者可能是它启用的特殊技巧(它确实启用了特殊技巧)。我不太清楚原点 是 是什么。关于它的事情是你不能忽视它。如果你尝试,Git 会时不时地伸出手来打你的脸:你好!你现在必须对我的索引大惊小怪了!所以有必要学会忍受它,即使你永远不会喜欢它,只要你会使用Git.
索引内容很多。特别是,如果您正在进行合并(或任何足以 merge-like 的事情)并且存在 合并冲突 ,Git 将 扩展 索引,这样它就不再只是一个暂存区。
不过大多数时候,描述 Git 索引的一个好方法是它是您 提议的下一次提交 。 Git 只需从 current 提交中填写此提议的 next 提交。然后,每次更新工作树中的某些文件时,您需要告诉 Git:将我更新的文件复制回您的索引,以便我对 有不同的建议next commit. 这就是 git add
所做的:它制作某个文件的索引副本,与该文件的工作树副本相匹配。
然后,当您 运行 git commit
、Git 可以而且确实会打包 在 索引中的任何文件——它们'以特殊的 Git-only 形式存储在索引中——这些是 Git 知道的文件。这就是快照的来源:当您或任何人 运行 git commit
.
5这是一个奇怪的例子,因为 Git 处于 Git 调用的 detached HEAD 模式.此模式用于实现 git rebase
,但它不是您通常使用的模式。通常 HEAD
会被 附加 。不过,恐怕我要跳过这里的大部分内容。
结论
作为要点列表:
Git 存储 提交 ,而不是文件。然后提交存储文件。这使得每个提交都可以说是一个 all-or-nothing 交易。
Git 发现 通过哈希 ID 提交:内部提交编号。
Git 使用 b运行ch names 找到 last (最最近)在一些提交链中提交。这样你通常不需要使用又大又丑的哈希 ID。
通常,Git 通过 添加新提交 来工作。新的提交应该指向现有的提交,以便它们添加。如果他们不指向后方,那么关于 b运行ch names 找到 last 提交的事情,以及 Git从那里向后工作,停止工作。
提交中的文件仅可用作存档(以及差异化,以及我们可以对 frozen-for-all-time 文件执行的所有其他操作)。要处理/使用文件,您必须提取 提交。
使用
git checkout
提取提交会填充 Git 的索引和您的 work-tree。您可以查看和处理/使用的文件在您的 work-tree 中,并且是 您的,而不是 Git 的.它们实际上 不在 存储库中:当您从复制出来的内容中更改它们时,Git 对此一无所知。事实上,您的 work-tree 文件不在 Git 中——它们就在旁边——这就是为什么你必须
的原因git add
;add
仅更新 提议的下一次提交 的事实是您必须git commit
.
其他一切都建立在上面之上。