(Git) 在不丢失当前更改的情况下将先前的提交推送到新的存储库
(Git) Push a previous commit to a new repository without losing current changes
我一直在开发应用程序。随着应用程序的扩展,创建我自己的代码库以供将来重用是常识,这样我就不必从头做起。
我创建了一个名为 MyCodeBase 的全新存储库。现在 我想将特定的先前提交推送到这个新分支,而不会丢失任何更改(未暂存的文件和未推送的提交) 我已经对当前存储库进行了更改。这个任务可以吗?
我尝试过的:
git push MyCodeBase <commit_SHA>:HEAD:main
(error: src refspec <commit_SHA>:HEAD does not match any)
git push MyCodeBase HEAD <commit_SHA>:main
(error: The destination you provided is not a full refname)
We tried to guess what you meant by:
- Looking for a ref that matches 'main' on the remote side.
- Checking if the being pushed ('<commit_SHA>') is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side. Neither worked, so we
gave up. You must fully qualify the ref. hint: The part of the
refspec is a commit object. hint: Did you mean to create a new branch
by pushing to <commit_SHA>:refs/heads/main
git push MyCodeBase <commit_SHA>:main
error: The destination you provided is not a full refname (i.e.,
starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'main' on the remote side.
- Checking if the being pushed ('<commit_SHA>') is a ref in
"refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.
Neither worked, so we gave up. You must fully qualify the ref. hint:
The part of the refspec is a commit object. hint: Did you mean
to create a new branch by pushing to hint:
'<commit_SHA>:refs/heads/main'? error:
failed to push some refs to
'https://github.com/<my_user_name>/MyCodeBase.git'
总结:
- 有:当前存储库有未提交的更改。空白存储库
- 想要:将之前的提交推送到空白仓库,而不丢失未提交的更改
TL;DR
你想要的是 git push <em>repository refspec</em>
语法,refspec
类似于:
a123456:refs/heads/main
请确保您确切知道它的作用(即阅读长篇)。
长
首先,在深入了解细节之前,请记住Git 存储库的定义是——或多或少1——一个提交集合。 Git 并不是真正的 关于 更改、文件、分支或我们用它做的任何事情,在根级别:在那个级别,这是关于 提交 .
这意味着您需要准确地知道提交是什么以及对您有何作用,这归结为以下几点:
每次提交都会存储一些文件集的完整快照。这些不是更改,这些是快照。它们类似于 tarball 或 zip 文件。如果您要像这样下载并解压一个档案,您不会将其视为“更改”。当然,您可以下载两个 档案并比较 它们以查找更改。同样,您不应该将 Git 提交视为更改——但是如果您有 两个 提交并且 compare 它们,您可以找到 更改。
同时,每个提交还存储了一些元数据:一些关于提交本身的信息。这包括提交人的姓名和电子邮件地址,以及一个 date-and-time 戳记。 (事实上,它有两组这样的数据:一组给作者,一组给提交者。)它有一个任意的 日志消息 ,无论谁提交,都可以写入,稍后告诉人们 为什么 特定提交存在。而且——对于 Git 至关重要——每个提交都有一定数量的 parent 提交哈希 ID 存储在其中。
每个提交都有一个唯一的哈希 ID。当我说 unique 时,我也不是说 有点独特 。我什至不是说“非常独特”,这在某些方面是对这个词的误用。作为一种目标,Git 的想法是为 每个 提交 任何人在任何地方所做的 一个哈希 ID,该哈希 ID 是特定于那个提交.2
一旦提交并获得了其唯一的哈希 ID,任何提交的任何部分都不能更改。
这个唯一的哈希 ID,以及提交的不变性质,意味着一个 Git 总是可以判断其他 Git 是否已经有 相同的提交,或者不是,只需让两个 Git 交换 哈希 ID。他们不需要交换整个文件集,甚至不需要交换文件的某些子集。哈希 ID 本身就说明了整个故事。
这样,hash ID 在某种意义上就是提交。你要么有散列 ID,在这种情况下你有提交,要么你没有,在这种情况下你会发现一些 Git - 任何 Git 任何地方 - 确实 拥有该哈希 ID 并从他们那里获得提交。
git push
和 git fetch
的意义在于确保 接收 Git— 对于 git push
,那就是“其他”Git;对于 git fetch
,那是 你的 Git——有部分或全部 commits sending Git想发
1稍后我也会谈到“或多或少”的部分。
2Git 允许在这个目标中失败,只要两个 Git 存储库中出现一些 non-unique 哈希 ID从未真正 遇见 。但是,Git 并没有试图猜测哪些存储库可能相互交换数据,哪些永远不会,而是试图让每个提交哈希 ID 普遍唯一。
Git 不“喜欢”stand-alone 提交
“Git 理念”,可以说,您始终拥有存储库的所有历史。但是, 存储库的历史究竟是什么?
如果我们再次查看提交的定义——存档加元数据,元数据包括 parent 或 parents[= 的原始哈希 ID 338=] 的提交——我们可以很快地描绘出这可能意味着什么:
first-commit <-... <-commit <-commit <-commit <-... <-last-commit
此处,来自提交的每个“箭头”实际上是较早 提交的哈希ID。我们说后面的提交 指向 前面的提交。
实际的散列ID是random-looking,而且又大又丑,人类根本记不住,3所以为了画图,我喜欢用大写代表哈希 ID 的字母:
A <-B <-C <--main
这是一张图购物中心存储库,其中只有三个提交。它也只有一个分支名称、main
。 name main
用于让 Git 知道三个提交中的哪个是 last 一个。
显然,在像这样的小型存储库中,我们可以只查看所有三个提交。一个——提交 A
——根本不指向回溯:这是第一个提交,但它不能。一个指向 A
,那一定是我们调用的第二个提交 B
,最后一个指向 B
,所以那一定是三个提交中的最后一个提交。但是在一个非常大的存储库中,可能会有成千上万的提交。找到“最后一个”会花费太长时间,并且还有其他缺点。因此 Git 添加了 分支名称 和其他名称,例如标签名称。这就是使存储库或多或少成为提交集合的原因:它实际上是提交和一些名称的集合,我们找到一些特定的提交。
分支名称特别查找分支的last 提交。这也是我们向存储库添加 提交的方式。如果我们在 main
,并且有:
A--B--C <-- main
然后我们添加一个 new 提交,新提交得到一些 random-looking,丑陋的唯一哈希 ID,我们称之为 D
。在 D
中,元数据包括 当前 提交 C
的哈希 ID(我们或 Git 通过名称 [=26] 找到=]).所以新提交 D
指向现有提交 C
。现在,为了使提交 D
成为该分支的 last 提交,Git 只需 写入 D
的哈希 ID ,不管是什么,改成名字main
:
A--B--C--D <-- main
现在我们的分支上有更多提交。
如果我们改为添加 new 分支,我们可能会从这个开始:
A--B--C <-- main, develop
如果我们是 on branch develop
,正如 git status
所说,我们 运行 git commit
并进行新的提交 D
,Git 以与往常相同的方式进行新提交,但是这次 Git 写入的 分支名称 是 develop
而不是 main
, 生产:
A--B--C <-- main
\
D <-- develop
请注意,Git 可以通过提交 C
做任何 有用的 ,比如显示 更改的内容 其中,Git 也需要提交 B
。要提交 D
,Git 需要先提交 C
。一般来说,Git 想要并且需要 每个历史提交 从 end-points 开始——其哈希 ID 在不同的分支名称中——并向后工作到第一个提交。
这通常意味着在大多数 Git 存储库中,4 你有 每次提交都导致最后一次提交 .这些提交 是 历史,在 Git 存储库中。没有“文件历史”这样的东西:每个提交都有 每个 文件的完整快照,作为一种存档。历史 是 提交集,Git 通过从分支名称的末端开始并向后查找。
3为了使它们具有普遍的独特性,这些都是必要的。
4Git 支持 so-called shallow 存储库,其中历史记录在某个时刻中断,但是一般来说,你不想使用这些。
这对您的 git push
意味着什么
当您 运行 git push
时,您是在告诉您的 Git 将一些特定的提交发送到其他 Git 存储库。其语法为:5
git push <repository> <refspec>
此处的 部分可以是 URL,或者 remote 的名称,例如 origin
。使用名称会增加各种便利功能。例如,Git 将找到使用该名称的 URL,这避免了必须重复键入相同的 long 和 error-prone URL。6
真正的魔力在于 部分。 A refspec 可以 是:
- 一个分支名称,本身;或
- 原始提交哈希 ID,后跟冒号,后跟 引用名称;或
- 单词
HEAD
,后接冒号,后跟 参考名称;
或更多选项,其中大部分我不会在这里讨论。您正在尝试使用中间或最后一个选项,并且在使用最后两个选项时,名称 通常必须是 完全限定的参考名称.我们稍后会回到这个话题,但在我们开始之前,让我们看看 git push
会做什么:
- 给定哈希 ID 或名称
HEAD
,您的 Git 将查找相应的提交。
- 您的 Git 然后会将该哈希 ID 提供给另一个 Git。如果他们已经提交了该提交,他们会对您的 Git 说:不,谢谢,我已经提交了。 这有一些含义。
- 如果y 没有,您的Git现在必须提供该提交的parent提交哈希ID。大多数普通提交只有一个哈希 ID;合并提交有两个或更多;和根提交有 none。无论提交的类型是什么,您的 Git 有义务提供 all parents.
这会重复,直到他们最终说他们确实拥有哈希 ID,或者您的 Git 运行 没有要提供的提交,因为您已经提供了导致并包括的所有历史提交你推的那个。
这允许您的 Git 知道您的 他们的 Git 已经拥有哪些文件。这就是我上面提到的含义。如果您提供承诺 C
,而他们 没有 ,但是您提供承诺 B
,他们 有 有那个,这告诉你的 Git 他们有提交 B
和 A
两者,因此他们有所有 文件 已经存在于提交 B
和 A
中。所以你的 Git 现在可以压缩你的提交 C
,知道他们已经提交 A-B
并且因此引用那些现有提交中的文件。
当然,如果他们没有这些提交——例如,如果这是一个新的 totally-empty 存储库——你的 Git 将必须发送 每次提交导致您发送的最后一个。
完成这一切后,您的 Git 现在要求他们的 Git 设置其中一个 他们的 名字。现在让我们结束本节,并正确描述 reference。
5语法不止一种;这是您在这里需要的通用文件。
6使用远程名称比这个 URL-shortening 添加了更多便利功能,但我将在此处介绍所有内容。
参考名称
我在上面提到 Git 使用每个 branch 名称来存储我们想要的 last 提交的哈希 ID say 是“在分支上”,而且 Git 有不止一种名称。其他类型的名称包括标签名称、remote-tracking 名称、处理 git stash
的“存储”等等。作为用户,您通常处理的三种名称是 branch 名称、tag 名称和 remote-tracking 名称如 origin/main
.
这些名字中的每一个都生活在 namespace 中。特别是分支名称位于 refs/heads/
下,而标签名称位于 refs/tags/
下。这意味着 branch main
实际上是 name refs/heads/main
。 标签 v1.2
实际上是refs/tags/v1.2
.
大多数时候,当您 运行 git push
时,您是在要求 Git 从一个或多个 分支 [=338] 发送提交=] 到其他 Git,当你这样做时,你希望他们设置 他们的 分支之一以记住 相同的最后一次提交 .当你这样做时,例如:
git push origin main
或:
git push origin develop
您通常希望他们设置同名分支。所以在这里,Git 让你可以省略 refs/heads/
部分 and 冒号 and 整个其他部分。您的 Git 计算出 main
实际上意味着 refs/heads/main:refs/heads/main
。也就是说,您希望您的分支名称 main
确定您将发送的 last 提交,然后您希望您的 Git 询问他们的 Git 设置 他们的 分支 名称 main
。
但是,您想要使用原始哈希 ID。那么,您的 Git 不知道是应该要求他们的 Git 设置一个 tag 名称,还是 branch 名称,或完全是其他名称。您需要做的是使用 fully-qualified 名称:
git push <url-or-remote> <hash>:refs/heads/somebranch
这将要求他们的Git创建或更新一个分支名称somebranch
Git 存储库,记住它的 last 提交,即您在 git push
行中使用其哈希 ID 的提交。如有必要,它将产生发送该提交 及其所有历史记录 的副作用。
您实际上无法推送未提交的更改
请注意,当您 运行 git push
时,您的 Git 发送的是 提交 。它将提交(带有元数据的快照)发送到另一个 Git,然后后者将它们暂时存储在隔离区中。您的 Git 不会发送 更改 ,而是发送整个提交。7 您的 Git 然后询问他们的 Git 创建或更新一些 reference 名称——分支名称、标签名称或任何其他你喜欢的名称——以便记住你在 push
中命名的特定提交.
如果您有未提交的代码,这些东西 不在 Git 中。您的Git字面上还不能发送。要发送它,您的 Git 必须先提交它。8 但是您会e 将到目前为止的整个提交历史发送到您的其他存储库。如果那是你想要的——通常是——你就很好:去吧。
7您的 Git 确实使用了压缩,这可能会将整个提交变成跨推送操作的更改——但是这些更改(如果有的话)取决于他们的 Git 有什么提交,你的 Git 知道。他们 Git 可能需要 re-expand 这些,然后 re-compress 他们,这取决于许多因素。
8当然,可以进行不在任何分支上的临时提交;然后您的 Git 可以发送这些。例如,temporary-commits-not-on-any-branch 就是 git stash
的工作方式。但是Git今天不这样做了,收货的Git需要用一些的名字来记住他们,也就是说我们又回到了整体参考规范问题。
我一直在开发应用程序。随着应用程序的扩展,创建我自己的代码库以供将来重用是常识,这样我就不必从头做起。 我创建了一个名为 MyCodeBase 的全新存储库。现在 我想将特定的先前提交推送到这个新分支,而不会丢失任何更改(未暂存的文件和未推送的提交) 我已经对当前存储库进行了更改。这个任务可以吗? 我尝试过的:
git push MyCodeBase <commit_SHA>:HEAD:main
(error: src refspec <commit_SHA>:HEAD does not match any)
git push MyCodeBase HEAD <commit_SHA>:main
(error: The destination you provided is not a full refname) We tried to guess what you meant by:
- Looking for a ref that matches 'main' on the remote side.
- Checking if the being pushed ('<commit_SHA>') is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side. Neither worked, so we gave up. You must fully qualify the ref. hint: The part of the refspec is a commit object. hint: Did you mean to create a new branch by pushing to <commit_SHA>:refs/heads/main
git push MyCodeBase <commit_SHA>:main
error: The destination you provided is not a full refname (i.e., starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'main' on the remote side.
- Checking if the being pushed ('<commit_SHA>') is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.Neither worked, so we gave up. You must fully qualify the ref. hint: The part of the refspec is a commit object. hint: Did you mean to create a new branch by pushing to hint: '<commit_SHA>:refs/heads/main'? error: failed to push some refs to 'https://github.com/<my_user_name>/MyCodeBase.git'
总结:
- 有:当前存储库有未提交的更改。空白存储库
- 想要:将之前的提交推送到空白仓库,而不丢失未提交的更改
TL;DR
你想要的是 git push <em>repository refspec</em>
语法,refspec
类似于:
a123456:refs/heads/main
请确保您确切知道它的作用(即阅读长篇)。
长
首先,在深入了解细节之前,请记住Git 存储库的定义是——或多或少1——一个提交集合。 Git 并不是真正的 关于 更改、文件、分支或我们用它做的任何事情,在根级别:在那个级别,这是关于 提交 .
这意味着您需要准确地知道提交是什么以及对您有何作用,这归结为以下几点:
每次提交都会存储一些文件集的完整快照。这些不是更改,这些是快照。它们类似于 tarball 或 zip 文件。如果您要像这样下载并解压一个档案,您不会将其视为“更改”。当然,您可以下载两个 档案并比较 它们以查找更改。同样,您不应该将 Git 提交视为更改——但是如果您有 两个 提交并且 compare 它们,您可以找到 更改。
同时,每个提交还存储了一些元数据:一些关于提交本身的信息。这包括提交人的姓名和电子邮件地址,以及一个 date-and-time 戳记。 (事实上,它有两组这样的数据:一组给作者,一组给提交者。)它有一个任意的 日志消息 ,无论谁提交,都可以写入,稍后告诉人们 为什么 特定提交存在。而且——对于 Git 至关重要——每个提交都有一定数量的 parent 提交哈希 ID 存储在其中。
每个提交都有一个唯一的哈希 ID。当我说 unique 时,我也不是说 有点独特 。我什至不是说“非常独特”,这在某些方面是对这个词的误用。作为一种目标,Git 的想法是为 每个 提交 任何人在任何地方所做的 一个哈希 ID,该哈希 ID 是特定于那个提交.2
一旦提交并获得了其唯一的哈希 ID,任何提交的任何部分都不能更改。
这个唯一的哈希 ID,以及提交的不变性质,意味着一个 Git 总是可以判断其他 Git 是否已经有 相同的提交,或者不是,只需让两个 Git 交换 哈希 ID。他们不需要交换整个文件集,甚至不需要交换文件的某些子集。哈希 ID 本身就说明了整个故事。
这样,hash ID 在某种意义上就是提交。你要么有散列 ID,在这种情况下你有提交,要么你没有,在这种情况下你会发现一些 Git - 任何 Git 任何地方 - 确实 拥有该哈希 ID 并从他们那里获得提交。
git push
和 git fetch
的意义在于确保 接收 Git— 对于 git push
,那就是“其他”Git;对于 git fetch
,那是 你的 Git——有部分或全部 commits sending Git想发
1稍后我也会谈到“或多或少”的部分。
2Git 允许在这个目标中失败,只要两个 Git 存储库中出现一些 non-unique 哈希 ID从未真正 遇见 。但是,Git 并没有试图猜测哪些存储库可能相互交换数据,哪些永远不会,而是试图让每个提交哈希 ID 普遍唯一。
Git 不“喜欢”stand-alone 提交
“Git 理念”,可以说,您始终拥有存储库的所有历史。但是, 存储库的历史究竟是什么?
如果我们再次查看提交的定义——存档加元数据,元数据包括 parent 或 parents[= 的原始哈希 ID 338=] 的提交——我们可以很快地描绘出这可能意味着什么:
first-commit <-... <-commit <-commit <-commit <-... <-last-commit
此处,来自提交的每个“箭头”实际上是较早 提交的哈希ID。我们说后面的提交 指向 前面的提交。
实际的散列ID是random-looking,而且又大又丑,人类根本记不住,3所以为了画图,我喜欢用大写代表哈希 ID 的字母:
A <-B <-C <--main
这是一张图购物中心存储库,其中只有三个提交。它也只有一个分支名称、main
。 name main
用于让 Git 知道三个提交中的哪个是 last 一个。
显然,在像这样的小型存储库中,我们可以只查看所有三个提交。一个——提交 A
——根本不指向回溯:这是第一个提交,但它不能。一个指向 A
,那一定是我们调用的第二个提交 B
,最后一个指向 B
,所以那一定是三个提交中的最后一个提交。但是在一个非常大的存储库中,可能会有成千上万的提交。找到“最后一个”会花费太长时间,并且还有其他缺点。因此 Git 添加了 分支名称 和其他名称,例如标签名称。这就是使存储库或多或少成为提交集合的原因:它实际上是提交和一些名称的集合,我们找到一些特定的提交。
分支名称特别查找分支的last 提交。这也是我们向存储库添加 提交的方式。如果我们在 main
,并且有:
A--B--C <-- main
然后我们添加一个 new 提交,新提交得到一些 random-looking,丑陋的唯一哈希 ID,我们称之为 D
。在 D
中,元数据包括 当前 提交 C
的哈希 ID(我们或 Git 通过名称 [=26] 找到=]).所以新提交 D
指向现有提交 C
。现在,为了使提交 D
成为该分支的 last 提交,Git 只需 写入 D
的哈希 ID ,不管是什么,改成名字main
:
A--B--C--D <-- main
现在我们的分支上有更多提交。
如果我们改为添加 new 分支,我们可能会从这个开始:
A--B--C <-- main, develop
如果我们是 on branch develop
,正如 git status
所说,我们 运行 git commit
并进行新的提交 D
,Git 以与往常相同的方式进行新提交,但是这次 Git 写入的 分支名称 是 develop
而不是 main
, 生产:
A--B--C <-- main
\
D <-- develop
请注意,Git 可以通过提交 C
做任何 有用的 ,比如显示 更改的内容 其中,Git 也需要提交 B
。要提交 D
,Git 需要先提交 C
。一般来说,Git 想要并且需要 每个历史提交 从 end-points 开始——其哈希 ID 在不同的分支名称中——并向后工作到第一个提交。
这通常意味着在大多数 Git 存储库中,4 你有 每次提交都导致最后一次提交 .这些提交 是 历史,在 Git 存储库中。没有“文件历史”这样的东西:每个提交都有 每个 文件的完整快照,作为一种存档。历史 是 提交集,Git 通过从分支名称的末端开始并向后查找。
3为了使它们具有普遍的独特性,这些都是必要的。
4Git 支持 so-called shallow 存储库,其中历史记录在某个时刻中断,但是一般来说,你不想使用这些。
这对您的 git push
意味着什么
当您 运行 git push
时,您是在告诉您的 Git 将一些特定的提交发送到其他 Git 存储库。其语法为:5
git push <repository> <refspec>
此处的 origin
。使用名称会增加各种便利功能。例如,Git 将找到使用该名称的 URL,这避免了必须重复键入相同的 long 和 error-prone URL。6
真正的魔力在于
- 一个分支名称,本身;或
- 原始提交哈希 ID,后跟冒号,后跟 引用名称;或
- 单词
HEAD
,后接冒号,后跟 参考名称;
或更多选项,其中大部分我不会在这里讨论。您正在尝试使用中间或最后一个选项,并且在使用最后两个选项时,名称 通常必须是 完全限定的参考名称.我们稍后会回到这个话题,但在我们开始之前,让我们看看 git push
会做什么:
- 给定哈希 ID 或名称
HEAD
,您的 Git 将查找相应的提交。 - 您的 Git 然后会将该哈希 ID 提供给另一个 Git。如果他们已经提交了该提交,他们会对您的 Git 说:不,谢谢,我已经提交了。 这有一些含义。
- 如果y 没有,您的Git现在必须提供该提交的parent提交哈希ID。大多数普通提交只有一个哈希 ID;合并提交有两个或更多;和根提交有 none。无论提交的类型是什么,您的 Git 有义务提供 all parents.
这会重复,直到他们最终说他们确实拥有哈希 ID,或者您的 Git 运行 没有要提供的提交,因为您已经提供了导致并包括的所有历史提交你推的那个。
这允许您的 Git 知道您的 他们的 Git 已经拥有哪些文件。这就是我上面提到的含义。如果您提供承诺 C
,而他们 没有 ,但是您提供承诺 B
,他们 有 有那个,这告诉你的 Git 他们有提交 B
和 A
两者,因此他们有所有 文件 已经存在于提交 B
和 A
中。所以你的 Git 现在可以压缩你的提交 C
,知道他们已经提交 A-B
并且因此引用那些现有提交中的文件。
当然,如果他们没有这些提交——例如,如果这是一个新的 totally-empty 存储库——你的 Git 将必须发送 每次提交导致您发送的最后一个。
完成这一切后,您的 Git 现在要求他们的 Git 设置其中一个 他们的 名字。现在让我们结束本节,并正确描述 reference。
5语法不止一种;这是您在这里需要的通用文件。
6使用远程名称比这个 URL-shortening 添加了更多便利功能,但我将在此处介绍所有内容。
参考名称
我在上面提到 Git 使用每个 branch 名称来存储我们想要的 last 提交的哈希 ID say 是“在分支上”,而且 Git 有不止一种名称。其他类型的名称包括标签名称、remote-tracking 名称、处理 git stash
的“存储”等等。作为用户,您通常处理的三种名称是 branch 名称、tag 名称和 remote-tracking 名称如 origin/main
.
这些名字中的每一个都生活在 namespace 中。特别是分支名称位于 refs/heads/
下,而标签名称位于 refs/tags/
下。这意味着 branch main
实际上是 name refs/heads/main
。 标签 v1.2
实际上是refs/tags/v1.2
.
大多数时候,当您 运行 git push
时,您是在要求 Git 从一个或多个 分支 [=338] 发送提交=] 到其他 Git,当你这样做时,你希望他们设置 他们的 分支之一以记住 相同的最后一次提交 .当你这样做时,例如:
git push origin main
或:
git push origin develop
您通常希望他们设置同名分支。所以在这里,Git 让你可以省略 refs/heads/
部分 and 冒号 and 整个其他部分。您的 Git 计算出 main
实际上意味着 refs/heads/main:refs/heads/main
。也就是说,您希望您的分支名称 main
确定您将发送的 last 提交,然后您希望您的 Git 询问他们的 Git 设置 他们的 分支 名称 main
。
但是,您想要使用原始哈希 ID。那么,您的 Git 不知道是应该要求他们的 Git 设置一个 tag 名称,还是 branch 名称,或完全是其他名称。您需要做的是使用 fully-qualified 名称:
git push <url-or-remote> <hash>:refs/heads/somebranch
这将要求他们的Git创建或更新一个分支名称somebranch
Git 存储库,记住它的 last 提交,即您在 git push
行中使用其哈希 ID 的提交。如有必要,它将产生发送该提交 及其所有历史记录 的副作用。
您实际上无法推送未提交的更改
请注意,当您 运行 git push
时,您的 Git 发送的是 提交 。它将提交(带有元数据的快照)发送到另一个 Git,然后后者将它们暂时存储在隔离区中。您的 Git 不会发送 更改 ,而是发送整个提交。7 您的 Git 然后询问他们的 Git 创建或更新一些 reference 名称——分支名称、标签名称或任何其他你喜欢的名称——以便记住你在 push
中命名的特定提交.
如果您有未提交的代码,这些东西 不在 Git 中。您的Git字面上还不能发送。要发送它,您的 Git 必须先提交它。8 但是您会e 将到目前为止的整个提交历史发送到您的其他存储库。如果那是你想要的——通常是——你就很好:去吧。
7您的 Git 确实使用了压缩,这可能会将整个提交变成跨推送操作的更改——但是这些更改(如果有的话)取决于他们的 Git 有什么提交,你的 Git 知道。他们 Git 可能需要 re-expand 这些,然后 re-compress 他们,这取决于许多因素。
8当然,可以进行不在任何分支上的临时提交;然后您的 Git 可以发送这些。例如,temporary-commits-not-on-any-branch 就是 git stash
的工作方式。但是Git今天不这样做了,收货的Git需要用一些的名字来记住他们,也就是说我们又回到了整体参考规范问题。