【问题标题】:Can git push to the current branch of a remote repository?git可以推送到远程仓库的当前分支吗?
【发布时间】:2016-01-03 07:03:00
【问题描述】:

来自https://stackoverflow.com/a/34535193/156458

当您推送到远程存储库的已签出分支时,您通常会收到警告,并且 Git 不允许您这样做

但我得到一个暗示,git 可以推送到远程存储库的当前分支,来自 Loeliger 的 Version Control with Git,2ed,尤其是粗体文本:

推送操作可以更新仓库状态,包括HEAD 提交。也就是说,即使远端的开发者已经完成了 没什么,分支 refs 和 HEAD 可能会改变,变得不同步 带有签出的文件和索引。

积极地在一个存储库中工作的开发人员 异步推送发生时不会看到推送。但随后的 该开发人员的提交将发生在意外的 HEAD 上,从而创建一个 奇怪的历史。强制推送将丢失来自另一个推送的提交 开发商。该存储库的开发人员也可能会发现自己 无法将她的历史记录与上游存储库或 下游克隆,因为它们不再是简单的快进 他们应该是。她不知道为什么:存储库默默地 从她下面变了出来。猫和狗会生活在一起。 会很糟糕。

因此,我们鼓励您只推送到裸存储库。 这不是一成不变的规则,但它是一个很好的指南 普通开发人员,被认为是最佳实践。有几个 您可能想要推入的实例和用例 开发存储库,但您应该完全了解它 影响。当您确实想要推送到开发存储库时, 您可能需要遵循两种基本方法之一。

在第一种情况下,您确实希望拥有一个工作目录 在接收存储库中签出一个分支。你可能知道, 例如,没有其他开发人员会积极参与 那里的发展,因此没有人可能是盲目的 支持将无声的更改推送到他的存储库中。

在这种情况下,您可能需要在接收中启用挂钩 用于执行某个分支的签出的存储库,也许只是那个 也推入工作目录。为了验证 接收存储库在具有自动 checkout,钩子应该确保非裸存储库的工作 目录不包含任何编辑或修改的文件,并且它的索引有 推送发生时没有处于暂存但未提交状态的文件。 如果不满足这些条件,您将面临失去这些条件的风险 在结帐覆盖它们时进行编辑或更改。

还有另一种情况,推入非裸存储库可以正常工作 好。根据协议,每个推送更改的开发人员都必须推送到未经检查的 out 分支 被认为只是一个接收分支。开发人员从不推动 预计要签出的分支。这取决于某些开发人员,特别是 管理签出的分支以及何时签出。也许那个人负责 处理接收分支并将它们合并到主分支中 签出。

这是否意味着 git 可以推送到远程存储库的当前分支? (我的猜测是肯定的,但我不确定)

最后一段之前的段落(建议使用钩子签出通过推送更新的分支的段落)是否假设要推送到的分支不是远程存储库中的当前分支? (我的想法是签出要推送到的分支意味着要推送到的分支不是当前分支,但最后一段指出了推送到未签出分支的“不同场景”,这暗示我上一段是关于推送到已签出的分支,即当前分支)

【问题讨论】:

  • 可以做这样的推送;允许什么取决于遥控器(通过该遥控器的配置条目)。因此,您引用的 SO 答案中的“通常”。 (没有时间详细说明。)
  • 谢谢。在最后一段之前,它建议使用一个钩子来签出通过推送更新的分支。它是否假设要推送到的分支是远程存储库中的当前分支?我的猜测是否定的。但如果是,这是否意味着签出当前的分支,即已签出?
  • 我不确定倒数第二个引用的段落是否是个好主意。我确实使用过类似的设置,但它有一些陷阱......

标签: git


【解决方案1】:

从 Git 2.3+ 开始,您可以配置接收端“在接收存储库中拥有一个带有签出分支的工作目录。”

具体来说:"push-to-checkout" (git 2.4, May 2015),它改进了"push-to-deploy" (Git 2.3, February 2015)

具体示例请参见“Deploy a project using Git push”。

  • 首先:推送部署 (git 2.3):

您可以将更改直接推送到服务器上的存储库。如果没有在服务器上进行本地修改,则对服务器当前分支的任何更改都将自动签出。即时部署!

要使用此功能,您必须首先通过运行在服务器上的 Git 存储库中启用它

$ git config receive.denyCurrentBranch updateInstead
  • 然后推送结帐挂钩 (git 2.4)

现在有一个push-to-checkout 钩子,可以安装在服务器上,以准确自定义用户推送到签出分支时发生的情况。
例如,默认情况下,如果服务器上的工作树发生任何更改,则此类推送将失败。 push-to-checkout hook 可以改为尝试将任何服务器端编辑与新的分支内容合并,或者它可以无条件地用推送的分支内容的原始副本覆盖任何本地更改。

简而言之,您可以直接推送到已签出的分支。
请参阅 commit 4d7a5ce 了解注意事项:

  • 仅更改工作树而不更改索引仍然是 更改受保护;

  • 工作树中将被覆盖的未跟踪文件 通过推送部署需要受到保护;

  • 恰好使文件与当前文件相同的更改 push 仍然是需要保护的更改(即功能的 清洁度要求比结账更严格)。

【讨论】:

    【解决方案2】:

    在这一点上,我会尝试回答,但我不想陷入你引用的书所说的细节中。

    我认为看待这个问题的方式是从“接收方”,即考虑当你是一个使用 git 的普通人并且有人推送到你的存储库时会发生什么(与你通常所做的相比,这是从他们那里取来的)。 Git为此提供了很多机制,以及一个可以正常工作的默认策略,即:“只是说不”。 :-) 较新版本的 git (2.3+) 增加了一些提供安全的策略,同时允许一些这些推送;您是否会称它们为“正确”或“完美”更多的是见仁见智。 (考虑一下如果有人开始在您的非裸“已部署”主机上进行编辑,然后,比如说,睡着了,这样其他人就无法推送它,因为它现在对 git “看起来很脏”。)

    首先请记住,可以将存储库设置为“裸”,这告诉 git 不要寻找工作树。 (您可以将非裸仓库转换为裸仓库,反之亦然,但根据我的经验,大多数人都会在此过程中搞砸一些事情。使用git clone --bare 最初会设置一个裸克隆,并避免创建工作树,这意味着这里没有混淆或错误的可能性。1) 给定一个没有工作树的存储库,没有人会进入它的工作树并做任何工作,并且“推送搞砸了”进度工作”的情况是不可能的。

    考虑到这一点,让我们看看当我们是推送的接收者时会发生什么,并且我们有一个非裸存储库,它有一个实际的工作树。我们还要记住两点:

    • 因为我们有一个工作树,所以我们还有一个索引文件 (.git/index),它通常扮演着“放置下一次提交的位置”和“缓存以加速 git status 和类似的双重角色”操作”。

    • 我们还有一个“当前”分支,保存在HEAD文件中,2我们可以直接查看,也可以使用git symbolic-ref HEAD读取(后者是正式认可的)方法)。如果当前分支是br,则HEAD 文件包含一行读取ref: refs/heads/br,例如,git symbolic-ref HEAD 打印refs/heads/br

      (如果我们处于“分离 HEAD”模式,HEAD 文件包含原始 SHA-1,而不是 ref: refs/heads/<em>branch</em>。在这种情况下,接收推送不会搞砸正在进行的工作,所以我们可以放心地忽略这种情况。)

    这是接收推送背后的底层机制:

    1. 作为接收者,我们首先接收要添加到存储库的对象。3我们添加所有这些对象,即使我们最终会拒绝一个或多个引用更新。

    2. 现在我们得到一个提案列表,其一般形式如下:“请将refs/heads/X 设置为1234567...”,“强制将refs/heads/Y 设置为fedcba9...”,等等。这些对应于启动push 正在使用的参考规范。 (如果提供的 SHA-1 是全零,他们的 git 要求我们删除这些引用。)

      我们会考虑这些参考更新请求中的每一个,部分是一次一个,部分是整个 gulps,应用下面列出的子规则。对于通过的更新,我们将提供的引用设置为提供的 SHA-1,并告诉另一个 git “ok,done”;对于失败的更新,我们告诉另一个 git “不,拒绝”并提供更多“原因”消息。 (我们还将钩子的 stderr 输出,有时是 stdout 输出传递给另一个 git。有一个小协议告诉他哪些是我们自己的答案,哪些只是传递的输出,以便他的 git 知道哪些更新我们接受了。)

    3. 一旦我们都完成了,我们会运行一个“post-receive hook”并将成功的更新传递给它(与 pre-receive hook 的形式相同,但会消除被拒绝的更新)。

    现在让我们(简单地)介绍一下用于接受或拒绝个别更新和/或整体更新的规则。这些不一定是实际的内部顺序(我将忽略一些特殊情况,例如receive.shallowUpdates):

    • 某些更新必须通过各种内置测试,最常见的是“快进”测试,有时还有“永不更改”测试。究竟哪些 refs 以哪种方式进行测试取决于我们的 git 版本、我们的配置和此更新的 force 标志。有关这些的详细信息,请参阅the git config documentation,特别注意receive.denyDeletesreceive.denyNonFastForwards,并注意在 git 1.8.2 之前,git 用于将快进规则应用于标记更新(但不是标记删除),当标记已更改为“从不更改”(但仍允许删除,除非设置了 receive.denyDeletes)。

    • 整个更新集被发送到pre-receive 钩子(在其标准输入上,作为一系列行)。它们首先增加了一点信息:与每个引用关联的当前 SHA-1,或者如果我们还没有该引用,则为全零。如果该钩子退出非零,则 整个更新集 - 整个推送 - 被拒绝。 (如果钩子不存在,我们认为这个测试已经通过了。)

    • 每个单独的更新都发送到update 挂钩(作为参数)。如果该钩子退出非零,则此特定更新被拒绝,但我们继续验证其他更新。 (和以前一样,如果钩子不存在,则测试自动通过。)

    • 最后,我们有“非裸存储库”规则,这是您在这里关心的,我将分开到他们自己的部分。

    更新非裸存储库:允许的内容和原因

    (我看到VonC beat me to this,但我会继续详细说明。)

    git 1.6.6、git 2.3 和 git 2.4 中的新配置条目或值是:

    • receive.denyDeleteCurrent:这个选项实际上是在 git 1.6.2 中引入的,但直到 git 1.6.6 才真正做任何事情。在此之前,无论当前分支是什么,接收到推送删除,都会删除当前分支的引用。当它这样做时,HEAD 指向一个不存在的分支(要修复它,您必须使用管道工具或直接修改文件)。在 git 1.6.6 中,默认情况下不允许这样做。 (我还没有测试过“坏 HEAD”的事情是否仍然发生。)

    • receive.denyCurrentBranch:这也在 1.6.2 中出现,并在 1.6.6 中启用(即默认的“拒绝”操作生效)。但是,它在 git 2.3 中增加了新的 'updateInstead' 值。

    请注意,这两个都是特定于“当前分支”的,即HEAD 所指的(单个)引用refs/heads/<em>br</em>。同样,它们仅适用于未设置 core.bare 的情况。在这种情况下,有一个工作树,其中包含在某种程度上与refs/heads/<em>br</em> 中归档的任何 SHA-1 相关的文件。还有(可能4)一个索引文件,它可能有也可能没有add-ed、rm-ed,如果你在中间,它会保持合并状态有冲突的合并。

    假设通过receive.denyCurrentBranch,您允许某人的git push 更改您的存储库中为refs/heads/br 存储的SHA-1。进一步假设您没有设置任何部署挂钩,并且没有使用新的(2.3+)功能。然后,在这种情况下,如果其他人更改了您的 refs/heads/br,您自己的索引和工作树将完全保持不变。具体来说,假设 br 曾经指向提交 2222222...,而其他人(例如 Bob)刚刚成功推送并将其更改为 3333333...

    如果您现在完成自己的编辑/合并/任何事情,git add 照常运行结果,然后运行git commit,git 将从您当前的索引进行一个新的提交,其中包括“提交2222222... 中的所有内容,除了你的git addgit rms”。 Bob 在3333333... 中所做的事情不在 在您的索引中。不过,git 所做的新提交将以 3333333... 作为其父级,同时使用从基于 2222222... 的索引中获取的 contents。结果是您的提交在添加所有更改的同时还原了 Bob 的所有更改:将您的新提交与 2222222... 进行比较将显示您所做的,而将您的新提交与其父项进行比较将显示您退出所有 Bob 的工作,同时保留您的自己的作品。

    如果您确实有一个钩子可以进行一些部署,那么您的索引和/或工作树的内容将完全取决于该钩子的作用。例如,如果它执行 git checkout -f,则 Bob 更改的所有内容都将替换您放入索引和工作树中的内容。

    这些结果都不是任何人真正想要的。

    新的updateInstead 设置更接近人们有时想要的:在允许参考更新之前(Bob 将refs/heads/br2222222... 更改为3333333...),git 检查您的索引和工作树是否匹配提交2222222...。如果他们这样做了,5 git 允许 Bob 的推送 并且 将该更新应用于您的索引和工作树,就好像您以某种方式发现 Bob 的推送并完成了 git checkout br,或者任何相当于使所有内容都更新的东西。

    这里仍然存在一些潜在的危险。例如,假设您已经打开 README 来处理它。您花了一段时间在编辑器中查找一些参考 URL 并输入它们,但没有在任何地方写出结果。同时,Bob 已经修复了README 并运行了他的git push。您的 git 看到您的工作树是“干净的”并且更新是“安全的”,因此它会更新您的 README

    根据您的编辑器有多聪明,当您写出您的README 时,您可能会覆盖 Bob 的更改,或者您的编辑器可能会说“嘿,README 已更改,我将获取新的”并且丢失您的工作等。有人可能会争辩说这是该编辑的不良行为(我会接受该论点),但这仍然是一个潜在的问题-不仅限于编辑;您可能正在运行一些缓慢的计算过程来写入您保持源代码控制的文件,这可能会产生相同类型的问题。

    Git 不会尝试决定如何处理这一切。 Git 只是为您提供配置选项(更多机制)并将最终的策略留给您。我想说 git 的默认设置是正确的;更高级的updateInstead 模式不是默认模式,因为“正确”策略不清楚。


    1还有其他可能的错误,这取决于您是否想要简单的 ssh 推送的组写入模式和共享。在以前的工作场所,我们最终制定了使用脚本配置可推送存储库的策略:您可以将希望在其中看到的内容设置为私有存储库,然后您自己运行脚本或让管理员运行它,给它您的私人仓库的 URL,通过克隆创建公共和共享仓库。在那之后,我们不在乎你对那个私人仓库做了什么,但这里的重点是我们将使用--bare 进行克隆,而不必让某人——通常是我——去修复所有的损坏位。 :-)

    2即使是一个裸仓库也有一个HEAD 文件,因此有一个当前分支。它也有一个索引,但没有工作目录,索引通常是无关紧要的。 (一些部署脚本最终使用了裸仓库的索引,这会导致其中一些部署脚本出现错误,但这完全是另一个问题。)当前分支有点相关:它会影响其他人的 git clone 将检查哪个分支它们在克隆过程结束时,只要它们没有指定特定的分支名称。

    3通常我们将这些作为“瘦包”,也就是说,可以对我们已有的对象进行增量压缩的包。为了实现这一点,在“接收对象”步骤之前有一个步骤,我们告诉发件人我们拥有哪些 SHA-1。您可以在发件人上使用git ls-remote 查看我们告诉发件人的内容。还有一些早期的协议协商步骤。这些对于较低级别的细节很重要,但对于上述过程不重要。

    4你可以删除.git/index,git会在以后需要的时候重新构建它。我不特别建议删除它,但如果您处于合并过程中,则会丢失所有存储的 git addgit rms,以及任何合并信息。

    5另外的测试也通过了(见VonC's answer)。其中一些额外的测试并没有在updateInstead 模式下进行初始测试,而且我认为是很难找到的。

    【讨论】:

    • 像往常一样具有指导性和详细性。 +1
    【解决方案3】:

    这是否意味着 git 可以推送到远程存储库的当前分支?
    (我的猜测是肯定的,但我不确定)

    这取决于你的 git 版本。
    在版本 2 之前,您必须明确告诉 git 推送到哪个分支,但从版本 2.X 开始,它发生了变化。

    所有详情都here:

    Git v2.0 发行说明

    向后兼容性说明

    git push [$there]没有说要推送什么时,我们使用了
    到目前为止的传统“匹配”语义(您的所有分支都已发送
    到远程只要已经有同名的分支 在那边)。

    在 Git 2.0 中,现在默认是“简单”语义,
    哪个推动:

    • 只有当前分支到同名分支,并且只有 当前分支设置为与该远程集成时 分支,如果您推送到与您获取相同的远程;或

    • 只有当前分支到同名分支,如果你 正在推送到您通常不从那里获取的远程。

    你可以使用配置变量“push.default”来改变 这。如果您是一位想要继续使用 “匹配”语义,您可以将变量设置为“匹配”,对于 例子。阅读文档以了解其他可能性。


    设置上游分支(给定分支的远程分支)

    您可以为每个分支设置默认(获取)远程分支,该分支将“交谈”到 (pull/push)。

    这些设置在每个分支的 .git/config 文件中定义。
    您也可以使用以下命令手动更改:

    git branch --set-upstream-to=upstream/foo
    

    【讨论】:

    • 他问的是接收方发生了什么,而不是发送方。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-17
    • 2019-07-29
    • 2011-09-10
    • 2016-07-08
    • 2013-02-21
    • 1970-01-01
    • 2015-08-29
    相关资源
    最近更新 更多