【问题标题】:How can I git push all changes to origin master without merging?如何在不合并的情况下将所有更改推送到原始主机?
【发布时间】:2020-07-11 07:25:29
【问题描述】:

Linux 上的 git-2.16.2

git新手,请多多包涵....

我克隆了一个 repo,更改了几个文件,现在我想将它推送到 origin master。我不想合并任何东西,我希望 git 在不合并的情况下接受我所做的所有更改。

我相信我仍然需要 git pull --rebase。我知道会有冲突。但是有没有办法绕过这个合并部分,只是将我所有的更改推回原始主机而不合并(在 git add 之后,c 的 git commit)?

【问题讨论】:

  • 您是否打算真正丢弃其他人在您的遥控器上推送的更改?如果没有,您需要变基或合并,并解决冲突,没有办法解决它。如果您确实想放弃遥控器上的这些更改,gut push --force 确实是您想要的。

标签: git


【解决方案1】:

你描述的是

git push --force

每个人都会告诉你不要那样做。也就是说,如果您曾经在存储库(或实际上是一个分支)上与某人一起工作。这是因为你是rewriting history

如果这是您自己的存储库(我认为是),那没问题。

如果您想“正确地”执行此操作,则必须进行合并,然后仅使用本地更改,例如 here。这样您就可以保留您的历史记录,并获得相同的结果。

【讨论】:

  • 所以 --force 会破坏其他任何人的更改,否则我将不得不与之合并?
  • 是的,没错。它实际上强制远程分支与本地分支相同。不管现在有什么。
【解决方案2】:

如果自克隆后原始状态发生了变化(例如,B 已推送到它),您不能只推送更改而不破坏 B 的更改。相反,您应该将本地更改放在他们自己的“新”分支上,将人员 B 的更改拉入您的主分支,然后将您的“新”分支重新定位到主分支的末尾。然后把它全部推回去。

【讨论】:

  • 我现在不需要这样做,但希望在其他人推送更改时做好准备(即使我告诉他们不要这样做,如果他们这样做了会破坏这些更改)。
【解决方案3】:

Git 不会推送更改,而是提交。那是因为 Git 也不存储更改。

每个提交都包含其所有文件的完整快照。这是提交的 data 部分。每个提交还包含一些元数据,例如谁做了它(姓名和电子邮件地址)、何时以及为什么(日志消息)。您可以通过将快照与之前提交的快照进行比较来查看提交作为更改,但它仍然是完整快照。

每个提交都有自己唯一的哈希 ID。该哈希 ID 意味着 that 提交:永远不会有任何其他提交,并且任何地方的其他 Git 存储库都不能将该提交哈希 ID 用于任何 other 提交。每个具有 this 提交的 Git 存储库都使用 that 哈希 ID。 (这就是为什么提交哈希 ID 如此庞大而丑陋的原因:它们必须是唯一的!)

同时,在每个提交的元数据中,一个提交包含一些较早的或 提交的原始哈希 ID。我们说这个提交指向它的父级。如果我们绘制几个提交的图表,我们会发现它们往往会变成向后看的线性链:

... <-F <-G <-H

Hlatestlast 提交。它有一些大而丑陋的哈希 ID,我刚刚称之为 H,而提交 H 包含早期提交 G 的实际哈希 ID,其中包含之前提交 F 的哈希 ID,并且很快。这些反向链使 Git 可以轻松地向您显示作为一组更改的提交,这比每次都向您显示整个快照要小得多(而且更有用)。

分支名称在 Git 中的作用是它让你和 Git 快速找到这个最后一次提交。我将停止绘制内部箭头箭头(因为懒惰),但继续将分支名称绘制为箭头:

...--F--G--H   <-- master

名称 master 包含最后一次提交 H 的哈希 ID。

我克隆了一个 repo,更改了几个文件,现在我想将它推送到 origin master。

从技术上讲,你克隆了 repo——这让你得到了他们所有的commits——并让你自己的名字master。您的克隆还记得 他们的 master 中最后一次提交的哈希 ID,在您的名字 origin/master 下,我们可以这样绘制:

...--G--H   <-- master (HEAD), origin/master

(这个HEAD 是Git 如何记住你正在使用的分支名称。)

当您进行新的提交时,它会获得一个新的、唯一的哈希 ID。让我们简称为I

...--G--H   <-- origin/master
         \
          I   <-- master (HEAD)

注意您的master 现在如何指向提交I,而不是H。但是你仍然可以通过两种方式找到HI 指向Horigin/master 直接指向H

同时,在他们的存储库中,也许有人添加了一个或两个新的提交:

...--G--H--J--K   <-- master

记住,这是他们的存储库(以及他们的master),而不是你的。

当您运行 git push origin master 时,您的 Git 会调用他们的 Git。你的 Git 告诉他们你有提交 I 并且你想把它交给他们。你的 Git 告诉他们I 的父级是H,他们说好的,我已经提交了,给我I。您将I 发送给他们:

          I   [your commit]
         /
...--G--H--J--K   <-- master

无论我们在上方或下方绘制I,还是将其更分支地绘制为:

          I   [your commit]
         /
...--G--H
         \
          J--K   <-- master

只要我们提取所有相关的提交及其连接。

现在你的 Git 要求他们——他们的 Git——设置 他们的 master 指向提交 I,你们现在都有了。 如果他们遵守这个礼貌的要求,他们将:

          I   <-- master
         /
...--G--H--J--K

他们将无法再找到提交J-K,因为他们的 Git 找到使用他们的分支名称的提交。这会找到I,然后返回到H,然后是G,依此类推,但从不转发。

如果您使用git push --force,则将您的礼貌请求更改为命令:设置您的master如果他们服从,他们将失去提交J-K

你应该怎么做?

如果他们有这些提交,您需要的是某种发送您的提交的方式,这样他们就不会丢失他们的

您有多种选择。一种是使用git merge origin/master 将您的工作与他们的组合。另一种是使用git rebase,比较复杂。

合并

要合并,请使用 git fetch 以确保您自己的 Git 与他们的 Git 是最新的,然后使用 git merge origin/master1 这会创建一个新的合并提交 ,我将其绘制为M

          I-----M   <-- master (HEAD)
         /     /
...--G--H--J--K   <-- origin/master

使合并提交成为合并的原因在于它指向多个父节点。这里的两个父母是提交IK。如果您现在让您的 Git 调用他们的 Git 并发送您的 master,您的 Git 将为他们提供提交 M。他们会接受M。由于M 有两个父母,你的Git 将提供IK。他们有K 但没有I,2 所以他们也会接受I,你的Git 会提供H。他们有H,所以最后他们会选择MI

然后,您的 Git 会要求他们将 master 设置为指向 M。这不会失去任何东西,所以他们可能会接受这个请求。你自己的 Git 现在会相应地更新你自己的 origin/master——你对他们的 master 的记忆。


1如果您真的喜欢git pull,您可以使用git pullgit fetch 步骤与git merge 步骤结合git pull 所做的只是运行这两个命令。这剥夺了您在两个命令之间插入 Git 命令的机会,而且对于 Git 新手来说,我发现这会让事情变得更加混乱,所以我更喜欢将它们作为单独的命令保留。

2他们可能仍然有你之前尝试的I,或者没有,这取决于他们的 Git 版本......以及你是否尝试过。如果他们确实有I,那很好:I 的哈希 ID 对于那个提交是唯一的。


变基

简而言之,git rebase 所做的是将一些提交复制到新的和改进的版本,然后移动一个分支名称。

没有什么——不是你也不是 Git 本身——可以更改任何现有的提交。所有提交都被冻结。这就是使哈希 ID 起作用的原因。但是您可以取出一个提交,并对其进行处理,并制作一个 new 提交,就其所做的更改而言,它与原始提交一样好,并且 比新的提交更好,因为它在正确的位置。

也就是说,假设你有:

          I   <-- master (HEAD)
         /
...--G--H--J--K   <-- origin/master

如果有一种简单的方法可以让 Git 复制 现有的提交 I 提交 K 之后的新的和改进的提交?

有,它是git rebase。我们运行:

git rebase origin/master

Git 列出了我们master 上的所有提交(IHG,... 在后面),然后从这个列表中删除所有在 origin/master 上的提交(KJHG,...)。这留下了提交I。 (如果我们有更多提交,例如I-L,我们将在此处拥有该列表,下一步将复制两个提交而不是一个。)

然后,我们的 Git 使用 分离的 HEAD 模式(我们将略过)复制每个提交,一次一个,在提交 K 之后进行,我们用origin/master命名的那个:

          I   <-- master
         /
...--G--H--J--K   <-- origin/master
               \
                I'  <-- HEAD

复制所有提交后,我们的 Git 然后将 我们的 名称 master 拉到最后复制的提交,并将 HEAD 重新附加到 master

          I   [abandoned]
         /
...--G--H--J--K   <-- origin/master
               \
                I'  <-- master (HEAD)

由于提交I 没有名称,我们不会直接找到它,并且由于I' 指向KJ 指向@987654426 @ 指向G,我们也不会这样找到它。似乎我们以某种方式更改了现有的提交 I

我们没有——I' 的哈希 ID 与 I 完全不同——但由于我们是唯一拥有I 的 Git,因此没有其他人需要知道这一点。我们现在可以运行了:

git push origin master

将提交I' 发送到另一个Git,并要求他们设置他们的 master 指向I'。只要他们的master 现在仍然指向K,这将成功。

【讨论】:

    猜你喜欢
    • 2020-08-04
    • 2013-10-18
    • 2019-06-13
    • 2016-10-02
    • 1970-01-01
    • 2018-05-20
    • 2020-09-07
    • 1970-01-01
    相关资源
    最近更新 更多