使用 Git 是否有相当于 Mercurial Push 命令?

Jon*_*onN 0 git mercurial push tortoisegit sourcetree

我已经使用TortoiseHg/Mercurial多年了,现在第一次使用 Git 与SourceTreeTortoiseGit GUI。从这个链接我了解到PUSH在Git上与Mercurial不同。 Mercurial PUSH仅影响远程存储库,但不影响工作区。 另一方面,使用 Git 进行 推送不仅会更新存储库,还会更新工作区(以及 Mercurial 没有的索引)。

我的问题是Git是否具有与 Mercurial PUSH等效的功能?我可以送到远程存储库并保持工作区和索引不变吗?我知道Git FETCH相当于 Mercurial PULL,因为它们都不会修改本地工作区。实际上,我正在寻找Git 的反向FETCH 。这是可能的还是我必须始终打开远程存储库并执行FETCH操作?

tor*_*rek 5

编辑:TL;DR 摘要:

\n\n
    \n
  • 使用 Git 时,您可以推送到人类使用的具有工作树的存储库。您不应该推送到该存储库中的当前分支,至少在没有特殊考虑的情况下是这样。所以 Git 默认阻止这个。

  • \n
  • Mercurial 允许您推送到此类存储库,包括推送到“当前分支”\xe2\x80\x94,这句话在 Mercurial\xe2\x80\x94 中的含义有所不同,因为在 Mercurial 中,Git 所称的“分离头”是正常且可用状态。这可以在 Git 中完成,只是不太实用。

  • \n
  • 因此,Git 提供并且您通常应该使用存储库作为推送目标。它们没有工作树,因此 Git 允许推送到它们,甚至推送到“当前分支”(Git 的概念,而不是 Mercurial 的概念)。

  • \n
\n\n

完整回复原文如下。

\n\n
\n\n

正如MrTux 所说,这种说法是错误的:

\n\n
\n

另一方面,使用 Git进行推送不仅会更新存储库,还会更新工作区(以及 Mercurial 没有的索引)。

\n
\n\n

在推送更新存储库时(像往常一样添加新提交),它至少不会、默认情况下不会更新索引,也不会更新工作树。(从 Git 2.4.0 开始,可以在 Git 版本中进行配置以执行此操作。)

\n\n

这里的问题是一些相当深刻的哲学概念之间的不匹配。对此的回答是:

\n\n
\n

我的问题是 Git 是否具有与 Mercurial PUSH等效的功能?我可以送到远程存储库并保持工作区和索引不变吗?

\n
\n\n

是“是的,使用分离的 HEAD”,但这个答案并不像您想象的那么有用。

\n\n

在 Mercurial 中,当前修订版本(称为.,与 Git 中的名称相对应HEAD)会被签出,并且可能会被修改,其方式与在 Git 中使用 Git“分离头”时的方式大致相同(尽管有一些重要区别)。索引本身\xe2\x80\x94存在并暴露在Git中,但不以这种形式存在,并且隐藏在Mercurial\xe2\x80\x94中,并不直接相关。

\n\n

问题在于,在 Mercurial 中,当前分支与当前修订版完全分开。在 Git 中,两者深深地交织在一起。显然,当前修订版位于 Mercurial 的某个分支上:所有修订版均位于 Mercurial 中的某个分支上。但在 Mercurial 中,所有修订都只在一个分支上。在 Git 中,修订\xe2\x80\x94a 提交\xe2\x80\x94 可能同时位于多个分支上:无、一个、两个、十个或数千个。虽然提交是不变且永久的(在 Git 和 Mercurial 中),但包含提交的分支集在 Mercurial 中是固定且不变的,但在 Git 中完全是流动的。

\n\n

Git(故意1)放弃了这样的想法:分支名称不仅仅是一个临时的、短暂的标签。分支名称的使用只是为了标识一个特定的提交,目前主要供人类使用,但也供在git push和期间使用git fetch

\n\n

因此,由于HEAD在 Git 中包含分支名称\xe2\x80\x94let\'s 将其称为cb当前分支 \xe2\x80\x94 并且分支名称包含提交 ID,因此如果我们将名称更改为,我们将遇到可怕的麻烦-ID 以意外方式映射。如果收到推送更新cb,当我们(通过HEAD使用它作为当前修订版时,我们会感到困惑:我们会相信我们的索引和工作树适用于cb 现在指向的提交,而不是cb 使用过的提交更新前点。

\n\n

同样,这在 Mercurial 中不是问题,因为在 Mercurial 中,系统知道哪个是当前版本。将新的修订(提交)推送到分支不会影响我们当前的修订,事实上,如果我们在没有更新的情况下进行新的提交,我们只会得到一个新的提交,它是一个新的头(小写,不是 Git 的全部)帽子的HEAD事情)。这个新头将没有名称,除非我们使用书签,但没有名称的头是 Mercurial 中的常见状态。我们只是跑去hg heads找这些未命名的头,并对hg update -r这些头进行修改(例如将它们合并,通常与其他头合并)。

\n\n

在 Git 中,你可以精确地得到这种状态:那就是“分离的 HEAD”。分离的 HEAD 直接包含提交 ID,而不是分支名称。现在,Git 不会丢失当前修订版(或者更确切地说,提交哈希 ID),因此可以安全地推送到此存储库。

\n\n

问题是,当你有一个分离的 HEAD 时,虽然你可以进行新的提交,但它们只能通过名称来记住HEAD。它们不在树枝上。(如果您愿意,您可以称为HEAD“匿名分支”。它有名称HEAD,但HEAD实际上不是分支名称,因为所有分支名称都半秘密地以 , 开头refs/heads/,但HEAD不是。)为了让 Git 更永久地记住它,您可以必须给这个东西一个分支名称:

\n\n
git checkout -b newbranch\n
Run Code Online (Sandbox Code Playgroud)\n\n

HEAD从“分离”变为“附加”,到新分支newbranch。分支名称newbranch现在存储提交哈希 ID,名称HEAD现在存储分支名称newbranch

\n\n

但现在你无法推动newbranch

\n\n

因此,只要HEAD不是“分离”,就有一些分支你不能,或者至少不应该推送到......并且为了HEAD正常工作,它最终必须重新附加到一个分支,也许是一个分支。

\n\n

请注意,您可以推送到任何其他分支。所有其他分支都不在索引中,也不在工作树中。

\n\n

Git 2.4.0 及更高版本中的新功能是您可以设置receive.denyCurrentBranchupdateInstead. 当这样设置时,接收端的 Git 会git push检查:

\n\n
    \n
  • 推送是否会发送到当前分支?也就是说,HEAD其中是否有分支的名称,如果有,推送是否会更新该分支名称?
  • \n
  • 如果没有(并且所有其他测试都通过),则允许更新。
  • \n
  • 如果是这样,请检查receive.denyCurrentBranch:\n\n
      \n
    • truerefuse:拒绝推送
    • \n
    • false或者ignore:接受推送,保留索引和工作树,依赖使用此存储库的人知道要做什么
    • \n
    • warn:接受推送,向正在执行推送的人打印警告(理想情况下,这是同一个人知道该怎么做,否则警告是无用的)。
    • \n
    • updateInstead:再检查一件事(见下文)。
    • \n
  • \n
\n\n

在这种updateInstead模式下,事情变得复杂。参见文档中git config描述以及文档push-to-checkout对钩子的描述。简而言之,如果索引和工作树是干净的,则通常会允许推送,并且接收存储库将更新到新的分支提示。githooks

\n\n

不过,一般来说,最好的选择是更简单的规则:只是不要这样做。2 不要推送到有人在其中工作的存储库。这是个坏主意。仅推送到从未有人在其中工作的存储库。将 Git 存储库配置为“裸”可以保证没有人可以在其中工作,因为它没有工作树。

\n\n
\n\n

1这要么是一个绝妙的想法,要么是一个糟糕的想法,或者可能两者兼而有之。无论哪种方式,它都是完全不同的。

\n\n

2这可以称为反耐克规则。:-)

\n