git push 命令与 master 和 main 冲突

git push command conflicts with master and main

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

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

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

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

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

git 添加.(不要忘记末尾的点)

git commit -m "初始提交"

git push origin main

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

  • 重命名您的本地分支:

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

    git push -u origin master:main
    

我敢肯定您会喜欢一个简单的公式,先做 X,然后做 Y,一切都会起作用。很遗憾,如果您没有提供更多信息,我们无法为您提供此信息。最后,我会提出一些可能性,但是 你必须检查你的提交,以及在 GitHub.[=199= 上的存储库中找到的提交]

怎么了

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

提交本身 已编号。这些提交编号或 哈希 ID 是 Git 真正 查找提交的方式。如果你有号码,那就是你真正需要的:把它给 Git 并且 Git 会找到提交,如果你有的话。这些数字的问题在于它们难以理解,人类无法处理。例如,2283e0e9af55689215afa39c03beb2315ce18e83 是 Git 存储库中 Git 提交的哈希 ID。没有人愿意记住这个!所以我们使用 名称 mastermain 来帮助我们记住它。

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

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

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

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

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

$ 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。这本身就很好——你可以使用任何你喜欢的名字,而且大多数人发现在他们的[=593]中使用相同的名字很有帮助=] 作为其他人(例如,GitHub)正在使用。然后你要求你的 Git 调用他们的 Git,将你的 main 分支提交发送给他们,并要求他们设置他们的 main 以记住你的 最后一次提交.

这里,我们需要顺便走一趟。我会尽量保持简短,但这里有很多知识!

提交内容

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

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

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

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

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

这些散列 ID 最终形成一个 backwards-looking 链。让我们画一个,使用单个大写字母代表 mind-bending 哈希 ID:

A <-B <-C

这是一个包含三个提交的简单链,正如您在 nearly-new Git 存储库中找到的那样。提交 A 是第一个提交。它没有任何更早的提交——它不能;这是 first 提交——所以它根本没有向后指向。但是,提交 B 第二个 提交,因此它向后指向 A,第一个提交。提交 C,当前是 last 提交,向后指向 B.

实际上,提交 C 字面上包含实际提交编号 - 哈希 ID - 早期提交 B,作为 C 元数据的一部分。同样,B 包含早期提交的哈希 ID A.

为了找到所有三个提交,Git只需要这三个提交的最后一个的哈希ID .我们已经 Git 将这个哈希 ID 粘贴到一个名称中,例如 master:

A--B--C   <-- master

如果我们将名称从 master 更改为 maincommits 的 none 更改:

A--B--C   <-- main

所以这很简单:我们可以随心所欲地更改我们的分支名称。我们选择的任何分支名称的 git checkout 实际上将 get 提交 C——无论它的真实哈希 ID 是什么——因为我们拥有的名称具有哈希 ID在其中提交 C

如果我们进行 new 提交,我们的新提交——我们称之为 D,尽管实际上它有一些丑陋的哈希 ID——将包含,作为其向后 link,提交的哈希 ID C:

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

这就是树枝生长的方式。从 DC 的反向连接表示提交 Dparent 是提交 CC的parent是B,以此类推,只是一旦我们打到A,就根本没有parent,一切都停止了。

GitHub 也有一个 Git 存储库

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

如果这些提交与您的相同的提交——例如,如果它们具有相同的A-B-C——你的状态会很好。您可以让您的 Git 呼叫他们的 Git。您的 Git 会列出哈希 ID D。他们会说:嘿,我没有 D,你为什么不给我那个? 你的 Git 会加上 D 到要发送的提交列表。那么你的Git会说D的parent是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 只能指向 one 最后一次提交,例如 HD。切换到 D 不会 添加到他们的提交中。所以他们拒绝了。

你是如何解决的

您需要做的是:

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

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

第 1 步很简单:只需 运行 git fetch。什么时候ne,你会有一个origin/main。这个名字不是 branch 名字,但它在大多数方面几乎一样好,并且在一个特定方面更好:这就是你的 Git 记忆 他们 Git的分支名称。每个 git fetch 你 运行 都会从他们那里得到任何新的提交,然后更新你对他们 Git 的 main 分支名称(和任何其他分支)的记忆。1

无论如何,这个 origin/main 可以让你看到他们的提交——现在也在 你的 存储库中,由 他们的相同哈希 ID 找到 Git 正在使用,因为每个 Git 同意 those 提交获得 those 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 调用 合并基础 。从这张图中可以看出,提交 H 最佳共享提交 。我们从两个分支端开始——提交 JL——然后开始向后工作,Git 总是这样做。从 J,我们回到 I,然后是 H,然后是 G,依此类推。从L,我们回到K,然后是HG等等。

这两个遍历 在提交 H 时相遇 。他们不断地从那里相遇,所以提交 H 自动成为两个分支相遇的 best(即最后一个)提交。合并操作然后找出自提交 H 以来 we've 更改的内容,以及 they 更改的内容,并合并这些更改。

我不知道你的 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

commit M 的特别之处在于,它不是只有一个 parent,例如 DH,而是 both[=其中 463=] 作为它的 两个 parent。所以它结合了两个独立的提交链。

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

git push -u origin main

您将发送给另一个 Git,提交 A-B-C-D-M。提交 M links 回到提交 H,这是 他们的 main 现在所在的位置。因此,如果他们服从您 Git 的礼貌请求,将他们的 main 设置为指向 M,他们也会保留他们的 G-H 提交。他们会宣布这很好。

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

组合方法#2:git rebase

rebase命令比较复杂,这里就不一一说明了。查看其他 StackOverf现在回答更多。相反,我只是注意到它通过 copying 提交来工作,就好像通过 运行ning 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 之后。如果我们调用AcopyA'B的copyB',等等,我们就可以绘制了结果是这样的:

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 查看您的提交时,您现在只有 6 个 个提交,而不是十个:git log 将显示您提交 D',然后是 C',依此类推。当它到达 A' 时,嗯,提交 A' 确实 有一个 parent:它 link 回到提交 H.所以在 A' 之后,git log 显示 H,然后 G,然后 然后 停止,因为提交有 运行出。

您现在可以运行:

git push -u origin main

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

结合方法#3:run roughshod超过它们

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

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

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

服从你的推送意味着他们拿走你的 A-B-C-D 链(或任何它是什么)并使他们的 main 指向这个链中的最后一次提交,就像你现有的 main.所以如果你有:

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

G--H   <-- origin/main

你force-push到GitHub(git push --force-with-lease -u origin main),他们会设置他们的main指向D。你最终得到:

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

G--H   ???

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

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

其实做这个动作并不难。我发现当我们初始化一个新的 GitHub 项目时,它会自动创建 main breach 作为默认分支。

然后我们可以使用以下命令将现有项目推送到 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