我在试着理解两者的区别

git push --force

and

git push --force-with-lease

我的猜测是,后者只推到远程,如果远程有提交,本地分支没有?


当前回答

Git push——force是破坏性的,因为它会无条件地覆盖远程存储库。Git的push -force是强烈反对的,因为它会破坏其他已经推送到共享存储库的提交。强制推送的最常见原因之一是当我们被迫重新设置分支时。

For example. We have a project with a feature branch that both Alice and Bob are going to work on. They both clone this repository and start work. Alice initially completes her part of the feature, and pushes this up to the main repository. This is all well and good. Bob also finishes his work, but before pushing it up he notices some changes had been merged into master. Wanting to keep a clean tree, he performs a rebase against the master branch. Of-course, when he goes to push this rebased branch it will be rejected. However not realising that Alice has already pushed her work, he performs a push --force. Unfortunately, this will erase all record of Alice's changes in the central repository.

——force-with-lease所做的是拒绝更新分支,除非它是我们所期望的状态;也就是说,没有人更新上游的分支。在实践中,这是通过检查上游的ref是否是我们所期望的,因为ref是哈希值,并隐式地将父链编码到它们的值中。

这里有一个关于git push -force和git push -force-with-lease的好帖子:-force被认为是有害的;理解git的-force-with-lease

其他回答

Force-with-lease is not necessarily safe. It just works as Sylvie said. One note: In git a branch is just a pointer on a commit. And commits point to zero or more parent commits. Even if you changed the branch entirely with a hard git reset and a forced push or a push with - - force-with-lease without wanting it, that's not necessarily a big problem. You can use your local git reflog to see how your local tip on the branches (Where was HEAD at that time? ) has changed and reset and push the branch again. Then you only lose new commits on the remote branch, but even they might be restored by the team members.

假设服务器上的任何pre-receive钩子都接受推送,这将总是成功:

git push --force

而这在继续之前会运行特定的客户端检查:

git push --force-with-lease

您可以自己手动运行特定的检查。下面是“租赁检查”算法:

计算出当前的分支。 运行git for-each-ref refs/remotes。注意git客户端认为对应于当前分支上游状态的commit-id。

例如,如果你在分支“foo”上,请注意与“refs/remotes/origin/foo”相关的commit-id。

现在就确定上游git服务器上远程分支的实际提交id。 只有在从第2步和第3步提取的commit-id同意的情况下,才允许“git推送”继续进行。换句话说,只有当本地git克隆的上游概念与实际的上游一致时,才继续执行。

这里有一个悲伤的暗示:由于git fetch将“refs/remotes/origin/*”下的所有引用更新到最新版本,这个命令组合本质上与git push——force相同:

git fetch

# The command below behaves identically to "git push --force"
# if a "git fetch" just happened!

git push --force-with-lease

为了解决git push -force-with-lease的这个固有弱点,我尝试从不运行git fetch。相反,当我需要与上游同步时,我总是运行git pull——rebase,因为git pull只更新refs/remotes下的单个引用,保持“租约”的——force-with-lease有用。

从可信的和/或官方来源寻找答案。

torek在评论和他的其他回答中提到的“比较和交换”在Git本身的来源中得到了进一步的说明。

后者只推送到远程,如果远程没有提交,本地分支没有?

该特性在本次提交中引入(2013年12月,Git v1.8.5-rc0)

——force-with-lease将保护所有将要被更新的远程引用,要求它们的当前值与一些合理的默认值相同,除非另有指定; 目前,“一些合理的默认值”暂时被定义为“我们拥有的用于更新远程的引用的远程跟踪分支的值”,如果我们没有这样的远程跟踪分支,这是一个错误。

所以"lease"的意思是:

“force-with-lease”:您假设在获取时在ref上获取了租约,以决定重基的历史记录应该是什么,并且只有在租约没有被破坏的情况下才能回推。

资料来源仍然提到了“cas”:

This option was originally called "cas" (for "compare and swap"), the name which nobody liked because it was too technical. The second attempt called it "lockref" (because it is conceptually like pushing after taking a lock) but the word "lock" was hated because it implied that it may reject push by others, which is not the way this option works. This round calls it "force-with-lease". You assume you took the lease on the ref when you fetched to decide what the rebased history should be, and you can push back only if the lease has not been broken.

"git push。force-with-lease vs.。force"

正如我在“push——force-with-lease by default”中提到的,Git 2.13 (Q2 2017)提到,如果后台进程(比如你在带有Git插件的IDE中发现的进程)运行Git fetch origin,则可以忽略——force-with-lease选项。 在这种情况下,——力占上风。

正如Pavlus在评论中补充的那样:

它本身并没有被忽略,只是现在你对本地远程头和远程头有相同的引用,所以——force-with-lease将表现正确,比较这两个,如果在取回和推送之间的时间间隔内,有人更新了远程,它不会表现为——force,它仍然会失败。


另一个不同之处是:在Git 2.29 (Q4 2020)之前,使用“——force-with-lease”选项压入名称包含非ascii字符的ref不能在智能HTTP协议上工作。 用git推力就行了。

参见brian m. carlson (bk2204)的commit cd85b44(2020年7月21日)。 (由Junio C Hamano - gitster -在commit c2796ac中合并,2020年7月30日)

make——force-with-lease作用于非ascii引用名称 报道:Frej Bjon 署名:布莱恩·m·卡尔森

When we invoke a remote transport helper and pass an option with an argument, we quote the argument as a C-style string if necessary. This is the case for the cas option, which implements the --force-with-lease command-line flag, when we're passing a non-ASCII refname. However, the remote curl helper isn't designed to parse such an argument, meaning that if we try to use --force-with-lease with an HTTP push and a non-ASCII refname, we get an error like this: error: cannot parse expected object name '0000000000000000000000000000000000000000"' Note the double quote, which get_oid has reminded us is not valid in an hex object ID. Even if we had been able to parse it, we would send the wrong data to the server: we'd send an escaped ref, which would not behave as the user wanted and might accidentally result in updating or deleting a ref we hadn't intended. Since we need to expect a quoted C-style string here, just check if the first argument is a double quote, and if so, unquote it. Note that if the refname contains a double quote, then we will have double-quoted it already, so there is no ambiguity. We test for this case only in the smart protocol, since the DAV-based protocol is not capable of handling this capability. We use UTF-8 because this is nicer in our tests and friendlier to Windows, but the code should work for all non-ASCII refs. While we're at it, since the name of the option is now well established and isn't going to change, let's inline it instead of using the #define constant.


Git 2.30 (Q1 2021)增加了Git push—force-if-includes

当一个本地分支,基于远程ref,重绕和力量推在远程,“——force-if-includes”运行检查,以确保任何更新remote-tracking裁判可能发生(通过推动从另一个存储库)之间的时间最后更新到本地分支(例如通过“git拉”)和前推的时候,已经集成本地之前允许强制更新。

Git push——force是破坏性的,因为它会无条件地覆盖远程存储库。Git的push -force是强烈反对的,因为它会破坏其他已经推送到共享存储库的提交。强制推送的最常见原因之一是当我们被迫重新设置分支时。

For example. We have a project with a feature branch that both Alice and Bob are going to work on. They both clone this repository and start work. Alice initially completes her part of the feature, and pushes this up to the main repository. This is all well and good. Bob also finishes his work, but before pushing it up he notices some changes had been merged into master. Wanting to keep a clean tree, he performs a rebase against the master branch. Of-course, when he goes to push this rebased branch it will be rejected. However not realising that Alice has already pushed her work, he performs a push --force. Unfortunately, this will erase all record of Alice's changes in the central repository.

——force-with-lease所做的是拒绝更新分支,除非它是我们所期望的状态;也就是说,没有人更新上游的分支。在实践中,这是通过检查上游的ref是否是我们所期望的,因为ref是哈希值,并隐式地将父链编码到它们的值中。

这里有一个关于git push -force和git push -force-with-lease的好帖子:-force被认为是有害的;理解git的-force-with-lease

强制用本地分支覆盖远程分支。

——force-with-lease是一个更安全的选项,如果向远程分支添加了更多的提交(由另一个团队成员或同事或其他什么人添加),它不会覆盖远程分支上的任何工作。它确保你不会通过强行推动而覆盖别人的工作。

我认为你关于命令的总体想法是正确的。如果远程分支与本地机器上的远程分支具有相同的值-您将覆盖remote。如果它没有相同的值—它表明在您处理代码时其他人对远程分支所做的更改,因此不会覆盖任何代码。显然,如果远程中有额外的提交,那么这些值就不会相同。

我只是认为force-with-lease是当我想要确保我不覆盖任何队友代码时使用的选项。我公司的许多团队使用——force-with-lease作为故障保险的默认选项。这在大多数情况下是不必要的,但如果你碰巧覆盖了另一个人贡献给远程的东西,它会为你省去很多麻烦。

我相信你看了文件,但这里可能有一些更啰嗦的解释:

https://git-scm.com/docs/git-push