首页 > 解决方案 > git 创建远程分支而不破坏任何现有分支

问题描述

我想在本地和远程创建一个开发分支,而不会意外干扰其他任何人的开发分支。创建本地分支很容易,并且不会受到任何竞争条件的影响,但是安全地创建远程分支却很棘手。

假设我想创建一个名为 的分支cleanup,但其他人可能有相同的想法并cleanup在我之前创建了自己的分支。如果我简单地说git push --set-upstream origin cleanup,也许它会创建一个新的远程分支,或者它可能会快进一个已经存在的分支。

如果远程分支已经存在,我想git push ...失败,所以我可以选择不同的分支名称。

我已经知道几个不完美的解决方案,比如git fetch然后快速推动;这仍然受制于比赛条件。或者做一个git push,注意命令输出中是否存在远程分支,然后尝试从不良情况中恢复;这很混乱,并且在恢复过程中会受到更糟糕的比赛条件的影响。也可以以git push --force-with-lease各种方式使用来做类似的事情,但我最接近解决问题的方法是拒绝创建分支而不是快速转发,这与我的目标相反。

更新:理想情况下,解决方案不会依赖于树中文件的数量或 git 历史记录的长度的算法复杂性。需要对 repo 进行新克隆或删除并重新签出工作树中的所有文件的解决方案是不可接受的。

标签: gitrace-condition

解决方案


function git-new-branch() {
    # usage: git-new-branch branchname
    git push origin $(git commit-tree -m "" $(git mktree <&-) <&-):refs/heads/$1 || return 1
    git checkout -b $1
    git push --set-upstream --force-with-lease origin $1
}

解释

git mktreegit commit-tree读取标准输入,因此用于<%-关闭这些进程的标准输入。

是幂等的git mktree并打印空树的 sha1。

使用git commit-tree空树进行空提交,重要的是该提交没有父母。git commit-tree也打印一个 sha1,但它不是幂等的。提交有你的user.name和一个时间戳。.git此外,此提交作为孤立提交进入目录。孤立的提交最终将被git自动垃圾收集;见git help gc。这-m ""意味着提交消息将为空。但是这些都不重要,因为这个提交将被使用一次并立即被放弃。

第一个git push将空提交推送到您提供的分支名称。如果分支已经存在,则保证被拒绝,因为空提交没有父母,因此推动它不可能导致快进。(并且由于每次调用都会重新生成提交,因此提交不可能已经在任何地方的远程仓库中。)

|| return 1如果命令失败,将中止该功能。

git checkout -b该命令没有错误检查。此函数假定您还没有具有给定名称的本地分支。

第二个git push使用--force-with-lease,这意味着只有当它仍然是我们认为的那样时,才会更新远程分支,也就是我们刚刚推送的空提交。

学分

感谢 o11c 和 jthill 的回答为这个解决方案提供了灵感。


推荐阅读