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。没有人愿意记住这个!所以我们使用 名称 像 master
或 main
来帮助我们记住它。
当你使用 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
更改为 main
,commits 的 none 更改:
A--B--C <-- main
所以这很简单:我们可以随心所欲地更改我们的分支名称。我们选择的任何分支名称的 git checkout
实际上将 get 提交 C
——无论它的真实哈希 ID 是什么——因为我们拥有的名称具有哈希 ID在其中提交 C
。
如果我们进行 new 提交,我们的新提交——我们称之为 D
,尽管实际上它有一些丑陋的哈希 ID——将包含,作为其向后 link,提交的哈希 ID C
:
A--B--C--D <-- main
这就是树枝生长的方式。从 D
到 C
的反向连接表示提交 D
的 parent 是提交 C
。 C
的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
。这意味着他们也有 A
和 B
所以你的 Git 必须发送的唯一提交是 D
.
然后,您的 Git 将只发送 D
,并要求他们设置 他们的 main
以记住 D
而不是 C
。那很好,因为 D
本身会记住 C
。您只是 添加他们的提交。
唉,这不是正在发生的事情。相反,他们有,例如:
G--H <-- main
在 他们的 存储库中。因此,如果您将整个提交系列发送给他们,他们将以:
A--B--C--D
G--H <-- main
然后您的 Git 要求他们将 main
设置为指向 D
。这将 失去他们的提交 。名称 main
只能指向 one 最后一次提交,例如 H
或 D
。切换到 D
不会 添加到他们的提交中。所以他们拒绝了。
你是如何解决的
您需要做的是:
- 获得他们的 提交。
- 决定如何合并您的提交和他们的提交。
第 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
是 最佳共享提交 。我们从两个分支端开始——提交 J
和 L
——然后开始向后工作,Git 总是这样做。从 J
,我们回到 I
,然后是 H
,然后是 G
,依此类推。从L
,我们回到K
,然后是H
和G
等等。
这两个遍历 在提交 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 继续并合并
这是否有效 取决于 D
和 H
中的 快照 中的内容(或无论你有什么最后的承诺)。如果遇到合并冲突,则必须自己解决合并冲突。一旦这一切都完成了——所有冲突都解决了,或者没有冲突——Git 可以创建一个新的 merge commit,我将其称为 M
for Merge
:
A--B--C--D
\
M <-- main
/
G--------H <-- origin/main
commit M
的特别之处在于,它不是只有一个 parent,例如 D
或 H
,而是 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
之后。如果我们调用A
的copy,A'
,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 push、git push -f
或 git 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 ???
提交 G
和 H
似乎消失了。它们实际上会在未来某个时候从 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
当我在终端上 运行 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。没有人愿意记住这个!所以我们使用 名称 像 master
或 main
来帮助我们记住它。
当你使用 git push
时,你是在告诉 你的 Git 调用其他 Git,在这种情况下,在 GitHub(origin
= https://github.com/randiltennakoon/my-bmi.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
更改为 main
,commits 的 none 更改:
A--B--C <-- main
所以这很简单:我们可以随心所欲地更改我们的分支名称。我们选择的任何分支名称的 git checkout
实际上将 get 提交 C
——无论它的真实哈希 ID 是什么——因为我们拥有的名称具有哈希 ID在其中提交 C
。
如果我们进行 new 提交,我们的新提交——我们称之为 D
,尽管实际上它有一些丑陋的哈希 ID——将包含,作为其向后 link,提交的哈希 ID C
:
A--B--C--D <-- main
这就是树枝生长的方式。从 D
到 C
的反向连接表示提交 D
的 parent 是提交 C
。 C
的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
。这意味着他们也有 A
和 B
所以你的 Git 必须发送的唯一提交是 D
.
然后,您的 Git 将只发送 D
,并要求他们设置 他们的 main
以记住 D
而不是 C
。那很好,因为 D
本身会记住 C
。您只是 添加他们的提交。
唉,这不是正在发生的事情。相反,他们有,例如:
G--H <-- main
在 他们的 存储库中。因此,如果您将整个提交系列发送给他们,他们将以:
A--B--C--D
G--H <-- main
然后您的 Git 要求他们将 main
设置为指向 D
。这将 失去他们的提交 。名称 main
只能指向 one 最后一次提交,例如 H
或 D
。切换到 D
不会 添加到他们的提交中。所以他们拒绝了。
你是如何解决的
您需要做的是:
- 获得他们的 提交。
- 决定如何合并您的提交和他们的提交。
第 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
是 最佳共享提交 。我们从两个分支端开始——提交 J
和 L
——然后开始向后工作,Git 总是这样做。从 J
,我们回到 I
,然后是 H
,然后是 G
,依此类推。从L
,我们回到K
,然后是H
和G
等等。
这两个遍历 在提交 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
.
这是否有效 取决于 D
和 H
中的 快照 中的内容(或无论你有什么最后的承诺)。如果遇到合并冲突,则必须自己解决合并冲突。一旦这一切都完成了——所有冲突都解决了,或者没有冲突——Git 可以创建一个新的 merge commit,我将其称为 M
for Merge
:
A--B--C--D
\
M <-- main
/
G--------H <-- origin/main
commit M
的特别之处在于,它不是只有一个 parent,例如 D
或 H
,而是 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
之后。如果我们调用A
的copy,A'
,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 push、git push -f
或 git 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 ???
提交 G
和 H
似乎消失了。它们实际上会在未来某个时候从 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