我多年来一直在使用 TortoiseHg / Mercurial ,现在我第一次使用Git使用 SourceTree 和 TortoiseGit GUI。从 link 我了解 PUSH 与 Mercurial 的 Git 不同。 Mercurial PUSH 仅影响远程存储库,但不会触及工作区。另一方面,使用Git PUSH 不仅更新了存储库,还更新了工作区(以及Mercurial没有的索引)。
我的问题是 Git 是否相当于Mercurial PUSH ?我可以推送到远程存储库并保留工作空间和索引吗?我知道 Git FETCH 相当于Mercurial PULL ,因为它们都没有修改本地工作空间。实际上,我正在寻找Git的反向 FETCH 。这是可能的还是我必须始终打开远程存储库并改为执行 FETCH ?
答案 0 :(得分:4)
编辑:TL; DR摘要:
使用Git时,可以推送到人类使用的存储库,它具有工作树。您只是不应该推送到该存储库中的当前分支,至少不是没有特殊考虑。所以Git默认阻止它。
Mercurial允许你推送到这样的存储库,包括#34;当前分支"这个短语意味着Mercurial中不同的东西 - 因为在Mercurial中,Git会称之为"分离HEAD"是一个正常和可用的状态。这个可以在Git中完成,它不太实用。
因此,Git提供,您通常应该使用 bare 存储库作为推送目标。这些没有工作树,因此Git允许推送它们,甚至是当前的分支" (Git的概念,而不是Mercurial的。)
以下原文完整回复。
作为MrTux said,这种说法是错误的:
另一方面,使用GitPUSH 不仅更新了存储库,还更新了工作区(以及Mercurial没有的索引)。
在推送更新存储库(像往常一样添加新提交)时,它至少不是默认情况下 - 更新索引,也不是工作树。 (它可以配置为从Git 2.4.0开始的Git版本中执行此操作。)
这里的问题是一些相当深刻的哲学概念之间的不匹配。答案是:
我的问题是Git是否相当于Mercurial PUSH ?我可以推送到远程存储库并保留工作空间和索引吗?
是"是的,使用分离的HEAD",但这个答案没有你想象的那么有用。
在Mercurial中,当前版本(称为.
,与Git中的名称HEAD
相比)已签出,并且可能已修改,其方式与Git中的相同。你正在使用一个Git"分离的HEAD" (虽然有一些重要的区别)。索引本身 - 存在并在Git中暴露的东西,但在该形式中不存在并且在Mercurial中被很好地隐藏 - 并不直接相关。
问题是在Mercurial中,当前的分支与当前的版本完全分开。在Git中,这两个是deeply intertwingled。显然,当前版本在某个分支上:所有版本都在Mercurial的某个分支上。但是在Mercurial中,所有修订都在上只有一个分支。在Git中,修订 - 提交 - 可以同时在许多分支上:无,一,二,十或数千。虽然提交是不变的和永久性的(在Git和Mercurial中),包含提交的分支集合在Mercurial中是固定不变的,但在Git中完全不变。
Git(故意 1 )抛弃了分支 name 不仅仅是临时的短暂标签的想法。分支名称的使用仅用于识别一个特定提交,目前主要供人类使用,但也可在git push
和git fetch
期间使用。
因此,由于Git中的HEAD
包含分支名称 -let' s为当前分支调用此cb
- 以及分支名称包含提交ID,如果我们以意想不到的方式更改名称到ID映射,我们将遇到麻烦。如果接收推送更新cb
,我们(通过HEAD
)使用作为我们的当前版本,我们会感到困惑:我们会相信我们的索引和工作树适用于cb
now 指向的提交,而不是cb
用于指向的提交在更新之前。
同样,这在Mercurial中不是问题,因为在Mercurial中,系统知道哪个是当前版本。将新版本(提交)推送到分支中不会影响我们的当前版本,事实上,如果我们在没有更新的情况下进行新的提交,我们只会得到一个新的提交,这是一个新的头部(小写,不是Git的全部大写HEAD
。这个新头将有 no 名称,除非我们使用书签,但是没有名字的头是Mercurial中的普通状态。我们只运行hg heads
来查找这些未命名的头,并hg update -r
来修改这些版本(例如将它们合并,通常与其他头部合并)。
在Git中,你可以准确地得到这种状态:"分离了HEAD"。分离的HEAD直接包含提交ID,而不是分支名称。既然Git不会输掉当前版本(或者更确切地说,提交哈希ID),那么推送到此存储库是安全的。
问题在于,当你有一个分离的HEAD时,当你可以进行新的提交时,它们只会被名字{{1}记住 }。它们位于 no 分支上。 (如果您愿意,可以拨打HEAD
"匿名分支"。它的名称为HEAD
,但HEAD
实际上不是分支 name,因为所有分支名称都以HEAD
半开始,而refs/heads/
没有。)为了让Git更永久地记住它,你必须给这个东西一个分支名称:
HEAD
更改git checkout -b newbranch
来自"分离"到"附加"到新分支HEAD
。分支名称newbranch
现在存储提交哈希ID,名称newbranch
现在存储分支名称HEAD
。
但现在你无法推进newbranch
!
因此只要newbranch
不是"分离",就有一些分支,你不能,或者至少不应该推动......以及HEAD
工作好吧,它最终必须重新附加到分支,也许是 new 分支。
请注意,您可以推送到任何其他分支。所有其他分支在索引中不,在工作树中不。
Git 2.4.0及更高版本中的新功能是您可以将HEAD
设置为receive.denyCurrentBranch
。当以这种方式设置时,updateInstead
接收端的Git检查:
git push
中是否有分支的名称,如果是,是否会更新此分支名称?HEAD
:
receive.denyCurrentBranch
或true
:拒绝推送refuse
或false
:接受推送,只留下索引和工作树,依赖于使用此存储库的任何人知道该怎么做ignore
:接受推送,向正在进行推送的人发出警告(理想情况下,这是一个知道该做什么的人,否则警告是没用的。)warn
:再检查一下(见下文)。在updateInstead
模式下,事情变得复杂了。请参阅the git config
documentation中的说明以及the githooks
documentation中updateInstead
挂钩的说明。简短的版本是,如果索引和工作树是干净的,通常会允许推送和接收存储库将更新为新的分支提示。
一般来说,最好的选择是更简单的规则:不要这样做。 2 不要推送到存储库人类在其中工作。这是个坏主意。仅推送到从不让人类在其中工作的存储库。将Git存储库配置为"裸"保证没有人可以在其中工作,因为它没有工作树。
1 这可能是一个好主意,也可能是一个糟糕的主意,或者两者同时发生。无论哪种方式,它都完全不同。
2 这可能被称为反耐克规则。 : - )
答案 1 :(得分:2)
Git push不会修改索引。
答案 2 :(得分:0)
虽然“不要推送到其中有人工作的存储库”,但另一个答案很适用于常规团队协作,有时您需要在私有存储库之间分发未完成的更改并在服务器上使用裸存储库对它来说是过度的。您可以通过推送到远程命名空间来处理推送到当前分支的问题,而无需处理:
push user@workstation:src/project 'refs/heads/*:refs/remotes/my-laptop/*'
这样当您返回workstation
时,您可以合并或挑选my-laptop/
命名空间中远程分支的更改。它还表明该工作已在另一个存储库中完成,以后应该更容易导航。