删除 git 历史记录中的副本/分叉

remove copies / fork in git history

我有以下 git 历史,不太确定我是怎么到那里的,但我似乎有一套 D-G 副本(git diff D D-copy 是空的)在我的 git 历史记录中创建了一个分支,有没有办法解决此问题以删除副本并在没有分支的情况下创建历史记录?

*   b4c394e (HEAD -> master, origin/master) I
|\
| * 17738ee G-copy
| * 6300744 F-copy
| * 62dbb41 E-copy
| * 951a5d9 D-copy
* | 05bfe4f H
* | 2701442 G
* | 242cf72 F
* | 2c9c7b6 E
* | b4bf474 D
|/
* 0f445d1 C
* ac8114f B
* 87759e6 A

在控制台中输入:

git rebase -i head~6

然后简单地删除或删除所有这些提交并通过键入保存:

:wq

I have the following git history, not really sure how i got there ...

我预测/相信,你到达了那里,通过使用 git rebase——可能以 git rebase -i 的形式,如 ——然后 git pull。 Rebase 本身很好,但您需要知道它的作用及其一些后果。

is there a way to fix this to remove the copies and create a history without the fork?

是的,但是在这个过程中对其他人有一些无关紧要的、次要的,或者可能相当重要的不愉快的后果……也就是说,如果 涉及 origin 存储库的任何其他用户。

rebase -i 方法可以很好地工作(再次参见 ),但更容易的是一个简单的方法:

git reset --hard HEAD~1

(但首先要确保,使用 git status,您不介意替换索引和工作树中的所有文件,即 git status 表示 nothing to commit, working tree clean ).使用 git log 检查结果以确保您喜欢它。

最后的关键步骤,无论您如何获得所需的提交集,都是使用 git push --force 的变体来更新 other Git 这里涉及到仓库。也就是说,最后,你 必须 指示另一个 Git,即原点的那个,丢弃一些 它的 提交.事实上 your Git 有你的 origin/master——你的 origin 的副本 master——意味着在这一点上,他们也有所有这些提交(也许还有更多!)。

如果 Git 存储库位于任何 URL 您通过名称 origin 记住的 Git 正在被其他人积极使用,您需要警告他们每当您强制共享 Git 存储库 丢失 一些提交时。如果没有其他 Git 存储库的其他用户,例如,如果那是你自己的私人 GitHub 副本,则没有其他人需要警告,所以你在这里没问题。

获得 "right" 历史记录后,您需要 运行:

git push --force-with-lease origin master

(相对安全,但您仍应警告任何其他用户)或:

git push --force origin master

(这是不太安全的,特别是如果这是一个共享存储库)。 -with-lease 变体使用你自己的 Git 的 origin/master——它对 他们的 Git 的 master 的记忆——确保他们的 Git 的 master 自从您上次有机会查看并决定是的以来没有改变,您确实希望他们忘记 他们的 提交。

更多细节

让我们再回顾一下这里的历史:

*   b4c394e (HEAD -> master, origin/master) I
|\
| * 17738ee G-copy
| * 6300744 F-copy
| * 62dbb41 E-copy
| * 951a5d9 D-copy
* | 05bfe4f H
* | 2701442 G
* | 242cf72 F
* | 2c9c7b6 E
* | b4bf474 D
|/
* 0f445d1 C
* ac8114f B
* 87759e6 A

git log --graph 的一个不错的属性是它通过将第一个父级绘制为下面的直接垂直来显示提交的第一对第二个父级——即 第一个 b4c394e(合并提交)的父级是 05bfe4f(提交 H)。 第二个17738ee(G拷贝)。

这向我表明你实际上做出或已经提交了 951a5d9(你在这里称之为 D-copy)、62dbb41630074417738ee——按照这个顺序——首先,或者用 git push 将它们发送给 origin 的另一个 Git,或者在 [=19] 从另一个 Git 得到它们=].

但是您可能不喜欢原始提交 D,您称之为 D-copy。也许它就像提交消息中的拼写错误一样简单。无论出于何种原因,您使用了一个提交,可能是 git rebase,告诉 您的 Git:我不喜欢 951a5d9然后 62dbb41 然后 6300744 然后 17738ee,我想提取 951a5d9,稍微改变一下,然后制作一个新的和改进的副本。

这个新的和改进的副本原来是 b4bf474:您现在称为 D 的提交。 Rebase 会自动复制所有后续提交,因为它必须:您调用 E 的原始 E-copy 的副本必须使用 b4bf474 作为其父项,因此它必须是具有某些内容的副本变了。新提交最终成为 2c9c7b6。出于同样的原因,您调用 F-copy 的提交必须复制到您调用 F 的提交,依此类推。最后,您有四个新的和改进的提交,按顺序从 b4bf474 开始并以 2701442.

结束

None 这些新的和改进的副本存在于 origin 的另一个 Git 存储库中。它仍然有 17738ee 作为它最后的 master 提交。您自己的 Git 已经扔掉了您的原件(您称为副本的那个),因为新的和改进的副本(您称为原件的那些)是新的和改进的。

然后,您提交了 H,这是全新的。然后你去发送你的新提交——新的和改进的,然后是全新的——到 origin 的 Git,git push origin master。但是无论出于何种原因,您 运行 git pullgit push origin master 成功之前。

(如果你运行git push origin master之前你运行git pull,其他Git,以上在 origin,说不。更具体地说,您的 Git 向他们发送了五个新提交,D-through-H,并要求他们更改名称 master 记住 H 作为 他们 b运行ch 上的最后一次提交。他们注意到这会丢弃 D-copy-through-G-copy。他们因此说 不,我会丢失一些提交 ,它作为 rejected (non-fast-forward) 错误消息出现,并提示您使用 git pull.)

不幸的是,git pull的意思是运行git fetch,然后运行git merge基于fetch看到的。 fetch 获取了他们的 master,这时候仍然标识 commit 17738ee。你的 Git 然后做了:

git merge -m "merge branch master of <url>" 17738ee

(或多或少),它创建了您在 git log 输出顶部看到的合并提交 b4c394e(提交 I)。该提交组合(合并)提交 05bfe4f17738ee。那么你 运行:

git push origin master

这一次,您向他们发送了他们还没有的提交,包括 I(不仅仅是 H),并要求他们将 master 设置为指向提交 I。他们没意见:没有 丢失提交 17738ee,因为从 b4c394e 开始并向后工作,他们仍然可以找到提交 17738ee:它是 b4c394e.

的第二个父级

不管你怎么做,你最终会做的是创建一些以提交结束的提交字符串 H——即,你现在拥有的相同哈希 ID——或一些新的和——提交 H 的改进副本(即一些不同的哈希 ID:哈希 ID 是提交的 真实 名称;主题和消息只是实际提交中的数据) .然后,您的 b运行ch 名称 master 将指向(存储哈希 ID)最后一次提交。提交本身存储了先前提交的哈希 ID,该哈希 ID 也被认为包含在 master b运行ch 中。上一次提交包含下一次提交的哈希 ID,依此类推。

一旦您的 master 拥有正确的哈希 ID,您将使用 git push 将您拥有但他们没有的任何新提交发送给另一个 Git在 URL 存储在名称 origin 下。该提交字符串在提交 05bfe4f 或其新改进的替代品处结束。然后你需要 order 他们——不仅仅是礼貌地询问他们——设置 他们 master 来持有与 你的 master 持有。这是 git push --force 步骤。

如果您使用简单的(和更旧的)git push --force,您的 Git 只需发送一个命令:设置您的 master!如果您使用较新的(但现在非常标准)git push --force-with-lease,您的 Git 会发送更复杂的命令:我认为您的 master 是 ______。如果是这样,请将您的 master 设置为 _____。告诉我你是否更新你的 master. 你的 Git 用适当的哈希 ID 填充这些空白。

他们的 Git 然后服从或不服从,并将结果发送给您的 Git。然后,您的 Git 要么告诉您推送成功,并更新您的 origin/master,要么告诉您推送失败,但没有。如果是因为哈希不匹配,它们的 master 已经改变,你应该 运行 git fetch 来更新你的 origin/master,然后看看到底发生了什么上。

我做了一个 git reset HEAD~1,现在历史再次干净(没有分叉),包含上次提交的更改,我将其添加为新提交并强制推送。我最近确实重命名了一个远程存储库,可能是问题所在。