Git: --force-with-lease 和多个 pushurls

14

我有一个Git仓库,远程origin在3个主机上进行了镜像。

$ git remote -v
origin  git@github.com:username/repo.git (fetch)
origin  git@github.com:username/repo.git (push)
origin  git@gitlab.com:username/repo.git (push)
origin  git@bitbucket.org:username/repo.git (push)

一切都在提交A的状态下,无处不在。

$ git rev-parse HEAD
A

$ cat .git/refs/remotes/origin/master
A

我提交了B分支并推送它,现在每个人都在B分支。

$ git push origin master
To github.com:username/repo.git
   A...B             master -> master
To gitlab.com:username/repo.git
   A...B             master -> master
To bitbucket.org:username/repo.git
   A...B             master -> master

$ git rev-parse HEAD
B

$ cat .git/refs/remotes/origin/master
B

我注意到上一个提交中有一个错误,所以我进行了修复并修改了提交记录。这使得我的本地代码与远程代码不同步。

$ git rev-parse HEAD
C

$ cat .git/refs/remotes/origin/master
B

我喜欢避免盲目地使用--force推送,所以我使用--force-with-lease,但是这种方式会出现有趣的失败情况。

$ git push --force-with-lease origin master
To github.com:username/repo.git
 + B...C             master -> master (forced update)
To gitlab.com:username/repo.git
 ! [rejected]        master -> master (stale info)
To bitbucket.org:username/repo.git
 ! [rejected]        master -> master (stale info)
问题是,只有当远程 ref 在上次我与之通信时的提交与本地记录在 .git/refs/remotes/origin/master 中的 sha1 相同时,--force-with-lease 才会考虑这个 push 是安全的。一旦第一个镜像(GitHub)被更新,git 就会将我的本地 remote ref 更新到提交 C,导致推送尝试到 GitLab 和 Bitbucket 失败,因为我们现在期望它们处于提交 C

我想搞清楚这个问题,所以首先我强制将 GitHub 镜像回滚到提交 B

$ git push origin +B:refs/heads/master
To github.com:username/repo.git
 + C...B             B -> master (forced update)
Everything up-to-date
Everything up-to-date

现在我需要更加具体地说明我希望将远程推送到哪个提交。文档显示,您可以使用 --force-with-lease=<refname>:<expect> 来精确指定要更新的引用和当前期望它所在的提交,因此我尝试了一下。

$ git push --force-with-lease=origin/master:B origin master
To github.com:username/repo.git
 ! [rejected]        master -> master (non-fast-forward)
To gitlab.com:username/repo.git
 ! [rejected]        master -> master (non-fast-forward)
To bitbucket.org:username/repo.git
 ! [rejected]        master -> master (non-fast-forward)

显然我做错了什么,也许是 <refname> 写错了?我感觉离成功很近了,但我错过了什么?


一个有趣的测试:git push --force-with-lease=$(git rev-parse origin/master):B origin master(即使用原始提交ID而不是名称)。我不确定Git在这种特殊情况下何时进行名称到ID的转换。 - torek
@torek 尝试了一下,但是没有成功。它做了同样的事情(所有推送都被拒绝为“非快进”)。 - ivan
1
有趣。我只是猜测(代码相当复杂),但我怀疑在多个URL期间,force-with-lease选项会在某个地方被遗忘。我敢打赌,如果你为每个URL只有一个远程,它会更好地工作。 - torek
@torek 我也开始怀疑了。用一个命令推送到所有三个远程库很方便,但或许也可以定义多个远程库来实现这一点。 - ivan
1
@ivan,你能否测试一下我的答案并查看它是否适用于你,或者我搞错了什么?谢谢! - Haralan Dobrev
@HaralanDobrev,我刚刚测试了一下,它完美地运行了。非常感谢你! - ivan
2个回答

13

非常有趣的问题!我已经尝试过了,使用 Git 2.11.0 在这些测试仓库中成功复制了你所描述的情况:

我可以通过以下形式成功地使用 --force-with-lease 推送到两个远程 URL:

git push --force-with-lease origin +master

注意分支名称前的+符号。以下是输出结果:

$ git push --force-with-lease origin +master
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 232 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:hkdobrev/git-test.git
 + 099b95f...08c7548 master -> master (forced update)
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 232 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:hkdobrev/git-test2.git
 + 099b95f...08c7548 master -> master (forced update)

来自git-push (1)手册页面:

请注意,--force适用于所有被推送的引用,因此将其与设置为匹配或使用多个推送目标配置的push.default一起使用可能覆盖除当前分支之外的其他引用(包括本地引用严格落后于它们的远程对应物)。要强制只推送到一个分支,请在要推送的refspec前面加上+(例如,git push origin +master强制推送到master分支)。有关详细信息,请参见上面的<refspec>...部分。


正如我在我的回答中指出的那样,我认为这基本上会导致正常的强制推送。 - Jörn Schellhaas

3
我想到的是:
git push --force-with-lease=`git rev-parse --abbrev-ref @{u} | sed 's![^/]*/!!'`:@{u}

何处

  • git rev-parse --abbrev-ref @{u} | sed 's![^/]*/!!' 获取远程分支名称(不包括远程名称)
  • @{u} 是我们期望远程分支所在的提交

我还尝试了使用 + 规范,如Haralan Dobrev建议的,但是可以通过它覆盖更改。因此,我认为在这里不应该使用 + 规范,因为它等同于正常的强制推送


如果我没记错的话,当我在 Git 2.11.0 上尝试时,“+”修饰符像预期的一样工作,并且租约选项也得到了尊重。 - Haralan Dobrev
1
我使用分支名称和远程名称使其工作,更易读: $ git push --force-with-lease="master:origin/master" "origin" "master" - Ross Patterson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接