【问题标题】:git push command conflicts with master and maingit push 命令与 master 和 main 冲突
【发布时间】:2021-02-21 13:20:34
【问题描述】:

当我在终端上运行git push -u origin main 时,它会显示error: src refspec main does not match any.

但是当我运行 git push -u origin master 时,它可以正常工作,它会在 repo 中创建另一个 master 分支以及默认的 main 分支,并且总分支将为 2。

此时,我无法进行 PR 将 master 合并到 main 并且错误消息是 there's no diff between master and main branches.

谁能解释这种情况并给我一个可行的解决方案?我们将非常感谢您的支持。

【问题讨论】:

  • 看起来您的本地存储库中没有名为“main”的分支。
  • 那么解决办法是什么?
  • 我不知道——你只能推送那些存在的分支。也许先创建分支?
  • 我没听懂。我认为我没有一个名为“main”的本地分支。
  • 是的,这就是为什么你不能推动它。我不清楚你想做什么。

标签: git main conflict master


【解决方案1】:

我很确定你会想要一个简单的公式,先做 X 然后做 Y,一切都会奏效。不幸的是,如果没有您提供更多信息,我们无法为您提供此信息。最后,我会提出一些可能性,但是你必须检查你的提交,以及在 GitHub 上的存储库中找到的提交。

出了什么问题

这里的最终问题是 Git 不是关于 分支。 Git 是关于提交的。分支名称确实很重要,因为分支名称可以帮助我们——以及 Git——找到提交。但真正重要的是提交。分支名称仅对查找提交很重要;在那之后,名称就不再重要了——这就是为什么您可以随时更改它们。

提交本身被编号。这些提交编号或 hash IDs 是 Git 真正 找到提交的方式。如果你有这个数字,这就是你真正需要的:把它交给 Git,如果你有,Git 会找到提交。这些数字的问题在于它们难以理解,人类无法处理。例如,2283e0e9af55689215afa39c03beb2315ce18e83 是 Git 存储库中 Git 提交的哈希 ID。没有人愿意记住这一点! ? 所以我们使用 namemastermain 为我们记住它。

当您使用 git push 时,您是在告诉 您的 Git 调用其他 Git,在这种情况下位于 GitHub(origin = https://github.com/randiltennakoon/my-bmi.git,根据 your comment)。你的 Git 和他们的 Git 进行了对话。你的 Git 列出了你的提交哈希 ID,他们列出了他们的(通过大量花哨的算法工作来最小化这种通信),这样你的两个 Git 就可以同意你有哪些提交,他们没有,你的 Git 想要发送他们。

然后,您的 Git 将这些提交发送给他们。他们将它们放在临时隔离区,同时检查您想做的其他事情。您的 Git 现在要求他们的 Git 设置一些他们的分支名称,以记住这些新提交。

你的 Git 可以让他们的 Git 设置他们的master。如果你告诉你的 Git 这样做,而他们没有master,他们通常会说“好的!”并去做。之后,他们将以他们的名字master 提交您的提交。但要做到这一点,你必须运行一个你还没有运行过的 Git 命令。

你的 Git 可以要求他们的 Git 设置他们的main。如果你告诉你的 Git 这样做,而他们确实有一个 main,他们会查看你刚刚发送的提交。这些提交是否添加到他们的main?还是这些新提交只是清除他们现有的提交,从他们的名字main 中找到?

这是您在该评论中稍后推送失败的地方:

$ git push -u origin main
To https://github.com/randiltennakoon/my-bmi.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/randiltennakoon/my-bmi.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

在这里,您将您的名字 master 更改为您的名字 main,以匹配他们的名字 main。这本身就很好——你可以使用任何你喜欢的名字,而且大多数人发现在 他们的 Git 中像其他人一样使用 相同 的名字是很有帮助的(例如,GitHub)正在使用。然后你让你的 Git 调用他们的 Git,向他们发送你的 main-branch 提交,并让他们设置他们的 main 以记住你的最后一次提交

在这里,我们需要进行一次侧面旅行。我会尽量保持简短,但这里有很多要知道的!

提交中有什么

我们已经提到,Git 是关于提交的,而且它们是有编号的。如果你运行 git log,你的 Git 会以相反的顺序显示你的提交——在你的情况下,所有提交,因为你只有一个分支——以及它们的哈希 ID(提交编号)。

这些数字看起来是随机的。它们实际上根本不是随机的:它们是使用来自提交的全部内容的加密哈希计算的。这就是 Git 确保每个提交都获得一个 唯一 编号的方式。但这也意味着任何现有提交的任何部分都不能更改。无论您现有的提交有什么编号,这些 都是 他们的编号。各地的每个 Git——你的,GitHub 上的那个,我的,Fred 的,每个 Git——都同意这些是提交的正确数字。没有其他提交可以在任何地方获得这些数字。这就是数字如此庞大和丑陋的原因。

现在,每个提交(无论其数量如何)都由两部分组成:

  • 提交的一部分是 Git 知道的每个文件的完整快照,在您或任何人进行提交时。也就是说,在您的git add .(或其他)和git commit 之后,Git 制作了它当时知道的所有文件的快照:这是您添加的任何文件,加上它已经知道的任何文件关于。 Git 目前实际上使用的是 Git index a.k.a. staging area 中那些文件的版本,而不是您可以查看和使用的版本,但我们会跳过这个细节。

  • 提交的另一部分是 Git 所称的 元数据,或有关提交本身的信息。在这里,Git 存储您的姓名、提交的作者以及您的电子邮件地址。 Git 添加了日期和时间戳。 Git 包含您的日志消息,您在其中解释为什么您进行此提交(不是其中的内容,我们可以从提交的快照部分看到,而是 为什么你做了你所做的一切,我们不能从快照中看到)。而且,对于 Git 本身至关重要的是,Git 添加了 previous 提交的哈希 ID。

这些哈希 ID 最终形成了一个向后看的链。让我们画一个,使用单个大写字母来代表令人费解的哈希 ID:

A <-B <-C

这是一个由三个提交组成的简单链,您可以在一个几乎全新的 Git 存储库中找到它。提交A 是第一次提交。它没有任何更早的提交——它不能;这是 first 提交——所以它根本不指向后面。然而,提交B第二次 提交,因此它向后指向A,即第一次提交。提交C,当前是last 提交,向后指向B

实际上,作为C 元数据的一部分,提交C 字面上包含先前提交B 的实际提交编号(哈希ID)。同样,B 包含先前提交 A 的哈希 ID。

为了找到所有三个提交,Git 只需要这三个提交中last的哈希ID。我们让 Git 将此哈希 ID 粘贴到名称中,例如 master:

A--B--C   <-- master

如果我们将名称从master 更改为main,则提交 都不会更改:

A--B--C   <-- main

所以这很简单:我们可以随意更改分支名称。我们选择的任何一个分支名称的git checkout 实际上会get 提交C——无论它的真实哈希 ID 是什么——因为我们拥有的一个名称中包含提交 C 的哈希 ID .

如果我们进行 new 提交,我们的新提交——我们称之为D,尽管实际上它得到了一些丑陋的大哈希 ID——将包含,作为它的反向链接,提交C的哈希ID:

A--B--C--D   <-- main

这就是分支的生长方式。从DC 的反向连接表示提交D 是提交CC 的父级是B,以此类推,只是一旦我们点击A,就根本没有父级,一切都停止了。

GitHub 也有一个 Git 存储库

同时,在 GitHub 上,通过 URL https://github.com/randiltennakoon/my-bmi.git 找到,另一个 Git 存储库。这个其他 Git 存储库有一些提交。这些提交有一些哈希 ID。

如果这些提交与您的相同的提交 - 例如,如果他们有相同的A-B-C - 你会处于良好状态。你会让你的 Git 调用他们的 Git。你的 Git 会列出哈希 ID D。他们会说:嘿,我没有 D,你为什么不给我那个? 你的 Git 会将 D 添加到提交列表中以发送过来。那么你的 Git 会说 D 的父级是 C。他们会检查并查看他们确实拥有C。这意味着他们也有AB,所以你的Git 必须发送的唯一提交是D

然后,您的 Git 将只发送 D,并要求他们设置 他们的 main 记住 D 而不是 C。这很好,因为D 本身记得C。您只是添加到他们的提交中

唉,这不是正在发生的事情。相反,他们有,例如:

G--H   <-- main

他们的存储库中。因此,如果您将整个系列的提交发送给他们,他们最终会得到:

A--B--C--D

G--H   <-- main

然后你的 Git 要求他们将他们的 main 设置为指向 D。这将失去他们的提交。名称main 只能指向一个最后一次提交,例如HD。切换到D 不会添加到他们的提交中。所以他们拒绝了。

你如何解决它

你需要做的是:

  1. 获得他们的提交。
  2. 决定如何组合你的提交和他们的提交

第 2 步是一个重大决定。这是我们无法为您制作的。

第 1 步很简单:只需运行 git fetch。完成后,您将拥有一个origin/main。这个名字不是一个分支的名字,但它在大多数方面几乎一样好,在一个特定方面更好:这是你的 Git 记住 他们的 Git 的分支名称。您运行的每个 git fetch 都会从他们那里获得任何新的提交,然后更新您对其 Git 的 main 分支名称(以及任何其他分支)的记忆。1

无论如何,这个origin/main 可以让你看到他们的提交——现在也在 你的 存储库中,通过 他们的 Git 使用的相同哈希 ID 找到,因为每个 Git 都同意 那些 提交获得 那些 ID。你现在有了他们的提交你的提交。这些不会相互关联,因为您的提交完全独立于他们的,而不是从他们的开始。

运行:

git log origin/main

查看所有他们的提交。他们有什么承诺?那些提交 in 是什么?它们重要吗?你应该保留这些提交吗?如果是这样,如何你想结合 你的提交与他们的?


1此处的确切细节取决于您运行的git fetch种类。例如,git pull 命令运行一个有限的git fetch,它不会获取所有内容。


组合方法#1:git merge

组合两个提交字符串的最简单方法是使用git merge。在你的情况下这仍然是正确的,但有一个障碍。

通常,使用git merge,我们从以下内容开始:

          I--J   <-- ours
         /
...--G--H
         \
          K--L   <-- theirs

也就是说,我们有一个分支——我们在这张图中称它为ours——在提交J处结束,并且他们有一个在提交L 结束的分支.我们让 Git 结合了这两个分支。 Git 通过查找提交 H 来实现这一点,Git 将其称为 merge base。从这张图中可以看出,提交H最佳共享提交。我们从两个分支末端开始——提交JL——然后开始向后工作,就像 Git 一直做的那样。从J,我们回到I,然后到H,和G,以此类推。从L,我们回到K,然后是HG等等。

这两个遍历相遇在提交H。他们不断地从那里开会,所以提交H 自动成为两个分支相遇的最佳(即最后一个)提交。然后,合并操作会找出自提交 H 以来我们已经 更改了什么,以及他们 更改了什么,并结合这些更改。

我不确切知道你的 Git 和他们的 Git 中有什么,但我确实知道,从你创建这两个 Git 存储库的方式来看,你拥有的和他们拥有的永远不会相遇时间>。你有类似的东西:

A--B--C--D   <-- main

G--H   <-- origin/main

如果我们从两端开始并向后工作,我们就永远不会相遇。

从 Git 2.9 版开始,git merge 默认不会合并这些。它只是抱怨历史无关,然后停止。您可以使用--allow-unrelated-histories 告诉 Git 继续合并。

这是否有效取决于DH 中的快照 中的内容(或您最后一次提交的任何内容)。如果您遇到合并冲突,您必须自己解决合并冲突。一旦这一切都完成了——所有的冲突都解决了,或者没有冲突——Git 可以创建一个新的 merge commit,我将它称为 M for Merge

A--B--C--D
          \
           M   <-- main
          /
G--------H   <-- origin/main

提交M 的特别之处在于,它不仅仅是一个父级,例如DH,它有both 作为它的两个父母。所以它结合了两个独立的提交链。

假设您想要制作M,并且确实制作M,您现在可以运行:

git push -u origin main

您将向另一个 Git 发送提交 A-B-C-D-M。提交 M 链接回提交 H,这就是 他们的 main 现在所在的位置。因此,如果他们遵守 Git 的礼貌请求,将他们的 main 设置为指向 M,他们也会保留他们的 G-H 提交。他们会裁定这很好。

提交M 中的内容作为快照 是合并两个提交的结果。如果您必须手动进行合并,因为合并冲突,选择提交M 的内容作为快照。如果 Git 能够自己进行合并,Git 将自己提交M,它会拥有你在D 中的所有内容,以及它们在H 中拥有的所有内容。

组合方法#2:git rebase

rebase 命令相当复杂,这里就不好好解释了。有关更多信息,请参阅其他 StackOverflow 答案。相反,我会注意到它通过 复制 提交来工作,就像通过运行 git cherry-pick 一样。

假设你确实有这个:

A--B--C--D   <-- main

G--H   <-- origin/main

如果您运行 git rebase origin/main,您的 Git 会将提交 A-B-C-D 的四个哈希 ID 保存在一个临时文件中。然后,您的 Git 将使用 git cherry-pick 或等效于 copy 每个提交,按照 A-B-C-D 顺序(向前!Git 必须将它们向后列出,然后反向列出来执行此操作),以在提交H 之后。如果我们调用AA'copy,以及BB'的副本等等,我们可以画出这样的结果:

A--B--C--D   <-- main

G--H   <-- origin/main
    \
     A'-B'-C'-D'   <-- ???

问号表明这里有一个问题:你的 Git 将如何找到副本 D'?答案是:它将 name main 从旧的提交系列中拉出,并将其粘贴到新系列的末尾。结果是:

A--B--C--D   ???

G--H   <-- origin/main
    \
     A'-B'-C'-D'   <-- main

现在很难找到旧的提交。如果您将他们的哈希 ID 保存在某处(在纸上、白板上、文件中等),您可以使用它们。 Git 还有其他技巧可以帮助您在一段时间内找到这些提交——默认情况下至少再找到 30 天——但在大多数情况下,提交 A-B-C-D似乎消失了强>。结果,当您使用 git log 查看您的提交时,您现在将只有 六个 提交,而不是十个:git log 将显示您提交 D',然后是 C' , 等等。当它到达A' 时,好吧,提交A' 确实 有一个父节点:它链接回提交H。所以在A' 之后,git log 显示H,然后是G然后因为提交用完而停止。

你现在可以运行了:

git push -u origin main

您的 Git 将发送提交 A'B'C'D',并礼貌地询问他们的 Git,他们是否会设置 他们的 main 指向到D'。由于D' 扩展了他们的分支,他们会说OK。

组合方法#3:run roughshod 在它们之上

假设,在查看他们的提交后,您认为这些提交完全没有价值。完全扔掉它们并没有什么坏处。

在这种情况下,您可以使用 Git 所谓的 强制推送git push -fgit push --force-with-lease--force-with-lease 版本增加了一些额外的安全性(通常是要走的路),但我不会在这里详细介绍这种安全性是什么。

这些选项的作用是告诉另一个 Git,在这种情况下,在 GitHub 上:我命令你,用我的提交哈希 ID 替换你的 main 也就是说,与常规推送不同,这不是一个礼貌的要求。这是一个直接的命令。他们不必必须服从,但如果您拥有该存储库,他们就会这样做。

服从您的推送意味着他们会使用您的A-B-C-D 链(或其他任何东西)并将他们的main 指向该链中的最后一个提交,就像您现有的main 一样。所以如果你有:

A--B--C--D   <-- main

G--H   <-- origin/main

如果你强制推送到 GitHub (git push --force-with-lease -u origin main),他们会将 他们的 main 设置为指向 D。你最终得到:

A--B--C--D   <-- main, origin/main

G--H   ???

提交 GH 似乎消失了。它们实际上会在未来某个时候从 GitHub 存储库中真正消失 - 很难说什么时候,因为这取决于 GitHub,但 30 天规则适用于你的 Git 不适用。它们可能会立即消失,或者在一天左右后消失。

因为您确实将它们放入您自己的(本地)Git 存储库,您的如果您认为它们很有价值,Git 将在至少 30 天内使用 G-H毕竟。 找到它们可能很棘手!我们(人类)使用名称而不是哈希 ID 是有原因的。

【讨论】:

  • 非常感谢您的解决方案。但是,我对此仍有疑问。以前 GitHub 默认不包含 main 分支。但是最近,我知道他们已经将master 更改为main
  • 所以,当我在 GitHub 中创建一个新的 repo 时,它会自动创建 main 分支作为默认分支。然后,当我们需要将现有项目推送到特定的 GitHub 存储库时,我们基本上要做的就是使用 git init 命令启动 git。然后使用git add &lt;file-name&gt; 添加文件,然后使用git commit -m "msg"
  • 然后git remote add origin &lt;URL&gt; 然后我们使用git push 命令。就我而言,我首先使用了git push -u origin master 命令,但它没有用。实际上,我想回到发生这个问题的开始,我希望你现在能知道我到底是哪里出了问题。
  • 问题是,如果没有提交,Git 分支无法存在。您(或某人)必须先进行至少一次提交。之后,可以创建无限数量的分支名称:所有分支都将标识单个第一次提交。一旦您(或某人,任何人)在该 Git 存储库中进行了 second 次提交,可能无限数量的分支名称都可以分别选择存在的 两个 提交之一;等等。
  • 这意味着GitHub要创建mainmaster,他们(GitHub)必须首先创建至少一个提交。一旦这个commit 存在(并且有一个提交号/hash-ID),then 他们可以创建一个名为mainmaster 的分支。您必须获得 this commit 并决定是保留它并在其上构建,还是丢弃它。其余的请参阅我的答案。
【解决方案2】:

听起来您的本地分支称为master,而远程分支称为main。你有两个选择:

  • 重命名您的本地分支:

    git checkout master
    git branch -m main
    git push -u origin main
    
  • 将本地分支推送到不同名称的远程分支:

    git push -u origin master:main
    

【讨论】:

  • 我尝试了第一个选项。但仍然给出同样的错误。 git push -u origin main To https://github.com/randiltennakoon/my-bmi.git ! [rejected] main -&gt; main (non-fast-forward) error: failed to push some refs to 'https://github.com/randiltennakoon/my-bmi.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
  • @RandilTennakoon:你的本地分支和远程分支有不同的提交。 “有关详细信息,请参阅 'git push --help' 中的'关于快进的说明'”(其他人已经推送或您正在处理完全不同的分支)
  • git 分支 > 主 git 分支 -r > origin/main
  • 我不知道如何解决这个问题,有什么分步指南吗?
  • @RandilTennakoon 你读过“关于快进的注意事项”吗?很难给出一般性建议,因为我不知道您的本地和远程历史记录如何。但是 Git 的帮助应该提供一个很好的概述
【解决方案3】:

实际上执行此操作并不难。我发现当我们初始化一个新的 GitHub 项目时,它会自动创建 main 漏洞作为默认分支。

然后我们可以使用以下命令将现有项目推送到 GitHub。

git init
git add <file-name>
git commit -m "msg"
git remote add origin <remote-URL>

接下来,在转到git push 命令之前,让我们运行git branch 命令来检查可用的分支。 git branch 显示本地分支,git branch -r 显示远程分支。

这是我的输出。

randiltennakoon@Randils-MacBook-Pro test_bmi_app % git branch
* master
randiltennakoon@Randils-MacBook-Pro test_bmi_app % git branch -r

您可以看到git branch -r 命令还没有输出。但它将master 显示为本地分支。

然后我们需要运行以下命令将master移动到main

git branch -m master main

然后您可以运行git push 命令和-f 标志,如下所示。

git push -u -f origin main

然后它将您的更改推送到远程main 分支,您可以通过转到您在 GitHub 中的存储库来验证它。

然后您可以发出以下命令进行验证,并且效果很好。

git branch
git branch -r

git branch -a

【讨论】:

  • 使用git push -u -f origin main 是我建议的解决方案#3,确定您要丢弃 GitHub 存储库。如果你不看就这样做,你就是在扔掉你没看过的东西:这是个好主意吗?
  • 是的,那行得通。感谢您的支持。你的信息帮助我找到了方向
  • 请注意,当您创建 GitHub 存储库时,您可以选择使用 no commits(并且没有初始分支)来创建它,这样就不需要 --force 选项.这可能是您想要的未来存储库的路线。或者,在创建 GitHub 存储库后,不要在本地使用 git initgit remote add,而是使用 git clone 将 GitHub 存储库复制到本地系统上的新存储库,这将获得初始提交他们以便有一个初始分支。 (GitHub 正在尝试通过为您创建此分支来提供服务——它是服务还是有害取决于您!)
【解决方案4】:

也许你只需要承诺。请运行以下命令;

git add .(不要忘记最后的点)

git commit -m "初始提交"

git push origin main

【讨论】:

猜你喜欢
  • 2021-04-13
  • 2011-05-23
  • 2014-02-01
  • 2023-02-02
  • 2016-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
相关资源
最近更新 更多