当遥控器上的分支历史发生变化时,您通常会得到
o git@git.server.com:XXXXX/Project.git
+ efe2e8b...cda0ee7 HEAD -> Ant_Config_processing (forced update)
有没有办法使用脚本来获得这个(强制更新)状态?
这个想法是编写一个别名来检测它并提示用户采取行动。
当遥控器上的分支历史发生变化时,您通常会得到
o git@git.server.com:XXXXX/Project.git
+ efe2e8b...cda0ee7 HEAD -> Ant_Config_processing (forced update)
有没有办法使用脚本来获得这个(强制更新)状态?
这个想法是编写一个别名来检测它并提示用户采取行动。
我有一个类似的问题,我想通了。
我想检测远程(裸)存储库中的钩子脚本中的强制更新,所以我的答案可能不适合原始问题,但我希望对未来的访问者有用。
https://github.com/kyanny/git-hooks-detect-force-update
这是一个示例 git pre-receive 钩子脚本,用于学习如何检测强制更新。
$ git rev-list oldrev ^newrev
$ rake -T
rake forced_push # git hooks test detect forced update
rake normal_push # git hooks test
在这种情况下,我们假设在具有此直接历史的 Git 工作存储库中。
1 --- 2 --- O --- X --- 3 --- 4 --- N
的一般用法git-rev-list
如下。
$ git rev-list N
此命令将显示从提交 N 可到达的所有提交(注意:git-rev-list
显示提交逆时间顺序)
git-rev-list
接受多个参数。
$ git rev-list N O
此命令将显示与 相同的输出git rev-list N
,因为提交 O 是提交 N 的祖先。
然后,git-rev-list
允许您从输出中排除提交。
$ git rev-list N ^O
^O 表示排除 O 可达的提交,因此该命令将显示 N, 4, 3, X (注意:排除 O)
由于我们了解了git-rev-list
,我描述了一个关于发生强制更新的案例。
在这种情况下,我们假设在具有这种复杂历史的 Git 工作存储库中。
* --- B --- * --- O ($oldrev)
\
* --- X --- * --- N ($newrev)
推送时,挂钩使用标准输入调用的预接收脚本。stdin 参数的格式在githooks(5)中有描述。
通常,我们从标准输入中提取两个提交对象 sha1 - oldrev 和 newrev。oldrev 是老树的HEAD,newrev 是新树的 HEAD 。
git-rev-list
在这种情况下,我们可以通过输出检测强制推送。
git rev-list oldrev ^newrev
显示可从 oldrev 访问但无法从 newrev 访问的提交。这表明提交只存在于 old tree。如果此命令显示任何提交,则旧树被新树替换,因此发生了强制更新。这就是我们想要的!
如果此命令显示没有提交,则新树已正常更新,因此不会发生强制更新。简单地。
一种方法是使用git reflog
,它会记录对分支的更改。
使用 reflog,您可以在 pull/fetch 之前获取您的分支指向的位置(如果它是脚本化的,我会使用 fetch,因为它不会自动合并)并检查该提交是否可以从新远程的“提示”访问分支。
使用 bash 你可以试试这个:
$ git rev-list remotename/branchname | grep $(git rev-parse remotename/branchname@{1})
$ echo $?
1
如果它返回一个散列(或退出状态 0),则意味着它在分支历史记录中找到了我们之前的分支提示,因此这是一个快进合并。如果它什么都不返回(或退出状态 1),它被强制更新。
您可以检查git reflog remotename/branchname
输出以查看分支名称是否得到强制更新。
$ git reflog remotename/branchname
dc2afab refs/remotes/remotename/branchname@{0}: fetch rewrite: forced-update
4603c2c refs/remotes/remotename/branchname@{1}: fetch rewrite: forced-update
You can use git-merge-base
command, it finds nearest common ancestor for two commits.
For fast-forward update, common ancestor for oldrev
and newrev
must point to oldrev
. Sample code to be put into pre-receive hook to block non fast-forward:
mergebase=`git merge-base $oldrev $newrev`
if [ "$oldrev" != "$mergebase" ]; then
echo "Non fast-forward update not allowed for $refname, from ${oldrev:0:16} to ${newrev:0:16} merge base ${mergebase:0:16}"
exit 1
fi