让我们假设我的同事和我正在同一个存储库中工作。我们从GitHub存储库中推送和提取提交。这是事件的时间表:
git commit --amend
,以修正master
中的最后一次提交。因此,他重写了提交历史。git pull
,则它将尝试将其覆盖的提交合并到我现有的提交历史记录中。git pull
,则会出现以下错误:fatal: refusing to merge unrelated histories
。我想做的是丢弃我的本地提交历史记录,并将其替换为GitHub上的提交历史记录。我知道我可以rm -rf .git
,但是随后我必须执行多个步骤,例如cd ..
,rm -rf repo
,git clone <repo-url>
。我正在尝试编写脚本,我正在寻找最简单的方法来使我的存储库与origin
保持同步,即使这意味着丢弃我的本地提交历史记录。
是否存在一个git
命令,可以轻松地使我们丢弃本地提交历史记录,并用origin
的提交历史记录替换它,而不管两个提交历史记录是相关还是不相关?>
答案 0 :(得分:2)
非常简单的命令:
git reset --hard origin/master
答案 1 :(得分:2)
这两个git reset --hard
答案是正确的,但可能有助于检查为什么正确以及它们的作用。
请记住,在Git中,提交代表所有文件的完整快照。 (嗯,它包含所有已提交的文件,但是这样说听起来很愚蠢,因为现在我们只是说“一个提交包含该提交包含的文件”。)但是它还具有 metadata ,例如谁进行提交(作者姓名和电子邮件),何时以及为什么(日志消息)。每个提交都由一些大的,难看的,显然是随机的哈希ID唯一标识。此外,每个提交中的元数据项之一是提交的 parent 的哈希ID。这些字符串在向后看的链中一起提交:
... <-F <-G <-H
“提交历史记录”只是一个提交,然后是其父提交,然后是下一个父提交,依此类推,它们都是按照从父到父的提交哈希ID链反向完成的。我们从结尾开始-在这种情况下,从哈希H
提交。这样就可以获取快照,还可以获取提交G
的ID。然后,我们移到G
,它获得了F
的ID,这使我们可以继续向后移动。 (最终,我们与父代( no )(该存储库中的第一个父代)进行了提交,这使我们停止了操作。)
但是:我们如何首先知道从H
开始?这是分支名称的来源。我们有一个类似master
的名称,在这个名称中,我们存储了 last 提交的哈希ID。案例H
:
...--F--G--H <-- master (HEAD)
运行git fetch
时,您的Git从其他Git获得提交。您的姓名master
,develop
等继续记录现有提交的哈希ID。您的远程跟踪名称-origin/master
,origin/develop
等-记录其提交的哈希ID。
尽管哈希ID看起来是随机的,但它们不是随机的!实际上,它们是通过对每个提交的所有内容(包括其父提交哈希)进行校验和来计算的。因此,如果我们的Git及其Git从相同提交开始 (例如,我们最初克隆了他们的提交),我们将共享 some 一组提交哈希:
A--B--...--H <-- origin/master
\
I--J <-- master (HEAD)
当我们提取他们的 new 提交时,它们的旧提交(A-B-...-H
)仍然存在,我们最终得到了一些以前没有的内容:>
K--L <-- origin/master
/
A--B--...--H
\
I--J <-- master (HEAD)
现在我们必须做一些事情来将我们的提交与他们的提交结合起来。
但是,如果我们不喜欢我们的提交,我们可以将我们的提交扔掉并使用它们。也就是说,我们可以告诉Git:停止查看提交J
,开始查看提交L
。我们让Git粗暴地拉我们 our master
,使其指向L
,而忘记了我们甚至曾经{em>曾经 I-J
链:
K--L <-- master (HEAD), origin/master
/
A--B--...--H
\
I--J [abandoned]
我们通过git reset
进行这种调整。
对于这种特殊情况(我们要清除索引和工作树并从要跳转到的提交中替换它们),我们添加--hard
。
Git使用附加到我们当前分支HEAD
的特殊名称master
来知道要提取哪个分支名称。在此示例中,只有一个分支名称,这很明显,但是当我们添加更多分支名称时,git reset
会影响附加了HEAD
的分支名称。
请注意,当我们添加 new 提交时,Git会执行相同的操作。新提交的父对象是HEAD
所指向的任何提交,或更准确地说,是HEAD
所指向的名称所指向的。然后,HEAD
所附加的名称,例如master
, moves! Git将其移至我们刚刚进行的新提交,这就是最后一次提交,我们将向后移动一次,一次提交,以查找历史记录。
如果您和其他人都从完全空白的存储库开始,并且进行了提交,则会得到:
A <-- master (HEAD)
他们得到了一些其他的哈希ID:
B <-- master (HEAD)
当您从他们那里git fetch
时,您也会得到他们的 提交,并且您将名称origin/master
设置为记住他们的master
,所以现在您有了:
A <-- master (HEAD)
B <-- origin/master
如果您现在git reset --hard origin/master
,Git会重新指向当前分支名称(master
,附加了HEAD
),以便它指向提交B
,您从他们那里得到了origin/master
指向的对象,因为origin/master
记得当您的Git调用他们的Git并要求他们提交最新内容时,他们的Git告诉了您的Git。所以现在您将拥有:
A [abandoned]
B <-- master (HEAD), origin/master
如果您想保留您的A
,只需在覆盖当前分支之前设置一个名称以记住该名称。也就是说,尽管您仍然记得A
的实际哈希ID,但请执行以下操作:
git branch save <hash-ID>
或者,只要master
仍然指向A
(您尚未完成git reset
):
git branch save master
然后您将拥有:
A <-- master (HEAD), save
B <-- origin/master
然后在git reset --hard origin/master
之后,您将拥有:
A <-- save
B <-- master (HEAD), origin/master
这里的规则非常简单:
提交永不更改。制作完成后,它们将永久冻结。
但是我们(人类)记住并找到了名称的提交,以及分支名称中的名称到哈希ID的映射,例如{{1} } 进行更改。
当我们进行新的提交时,Git自动更新当前分支名称(基于master
的附件)。
HEAD
以任意方式更改当前分支名称。
如果您为提交丢失了 only 名称,则和不在其他名称的历史记录中(因此您无法通过向后跟踪过程找到它),Git最终(通常在30天左右的某个时间后)会丢弃提交本身。因此,如我们通过git reset
那样放弃一个提交或一连串的提交,最终可能会冻结冻结的提交。 (因此,如果要保存/保存它们,请确保在链中的 last 提交中使用一些名称。)
这意味着一直冻结,在第一个要点有点夸张:只要提交本身存在,就保持 。但是,就像说“提交快照保存提交快照所保存的文件”一样,说这种话似乎有些言过其实。说永远冻结一次提交并不是100%正确,但是已经足够接近:只要提交本身继续存在,它就会被冻结。
提交(由哈希ID标识)是冻结的,静态的,不变的;诸如git reset
之类的分支名称不断变化并且不断变化。您可以根据需要移动分支名称,以使冻结的提交可查找。只要需要创建更多历史记录,就添加 new 提交。当您想忘记一些历史记录时,您可以将它们从现在的位置粗暴地拖到其他地方。
哈希ID是通用的:它们在每个 Git存储库中是永远相同的(嗯,曾经与 this Git存储库共享的每个Git存储库)。 分支名称特定于此特定的Git存储库!
答案 2 :(得分:1)
您要做的最好选择是使用git reset --hard origin/master
将HEAD
设置为origin/master
。