0. 文章背景
在日常工作中,有时候我们需要将完整的AOSP源码汇入私有gerrit中,以方便各类开发人员拉取,由于AOSP源码涉及的repo仓库多(截止笔者发稿,AOSP关联仓库已达到1100多个),一次性汇入gerrit耗时长,难度大,尤其是在源码汇入gerrit中,遇到了诸多问题,为了解决这些问题,我参考前人的相关文档《将安卓代码导入gerrit》,修改了汇入gerrit的shell脚本,在此分享给大家,以便后人汇入gerrit时少走弯路。
为了方便AOSP 开发者初次使用,我将aosp相关源码写成了shell工具,读者可以自行取用,该工具还在迭代中,欢迎各位读者PR。
仓库地址:github.com/zyf41919431…clone该项目后执行
bash main.sh,按照提示操作,示例:
0.1 先期准备
执行gerrit汇入您至少需要先具备如下环境
- 拥有一份完整的AOSP源码,如需拉取可参考 《Android11源码编译教程与排错指南》
- 拥有gerrit账户
- gerrit允许您的账户进行push操作

【图】gerrit AOSP仓库一揽
1. gerrit汇入AOSP流程
1.1 创建repo仓库群
原始AOSP仓库具有1100多个仓库,这些仓库需要在私有gerrit中创建,除此之外,我们还需做如下操作:
- 新建一个
manifests仓库以保存我们的manifests文件 - 新建一个
AOSP仓库作为父仓库,用于gerrit权限管理
您可以通过如下脚本进行创建:
注意
- 请在AOSP源码根目录执行该文件
- 请对
<>标识的内容按实际情况替换- 该脚本执行结果幂等,可以重复执行而不影响最终结果
LOCAL_PATH=`pwd`
MANIFEST_XML_FILE=$LOCAL_PATH/.repo/manifests/default.xml
USER_NAME="<Your gerrit username>"
SERVER_IP="<1.1.1.1>"
SERVER_PORT="<29418>"
OUTPUT_PROJECT_LIST_FILE_NAME=$LOCAL_PATH/project_list_name
OUTPUT_PROJECT_LIST_FILE_PATH=$LOCAL_PATH/project_list_path
function getNameAndPath()
{
echo > $OUTPUT_PROJECT_LIST_FILE_NAME
echo > $OUTPUT_PROJECT_LIST_FILE_PATH
while read LINE
do
command_line=`echo $LINE | grep "<project"`
if [ "$command_line" ]
then
#echo $LINE
reposity_name_sec=${LINE#*name=\"}
reposity_path_sec=${LINE#*path=\"}
if [ "$reposity_name_sec" ] && [ "$reposity_path_sec" ]
then
reposity_name=${reposity_name_sec%%\"*}
reposity_path=${reposity_path_sec%%\"*}
echo "$reposity_name" >> $OUTPUT_PROJECT_LIST_FILE_NAME
echo "$reposity_path" >> $OUTPUT_PROJECT_LIST_FILE_PATH
fi
fi
done < $MANIFEST_XML_FILE
}
function creatEmptyGerritProject()
{
# 建立父仓库 AOSP,方便gerrita权限管理
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project --permissions-only AOSP"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project --permissions-only AOSP
# 建立单独的 manifests 仓库,风格与AOSP保持一致
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project platform/manifests"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project platform/manifests
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit set-project-parent --parent AOSP platform/manifests"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit set-project-parent --parent AOSP platform/manifests
for i in `cat $OUTPUT_PROJECT_LIST_FILE_NAME`;
do
echo $i
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project $i"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit create-project $i
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit set-project-parent --parent AOSP $i"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP gerrit set-project-parent --parent AOSP $i
done
}
function removeFiles()
{
rm -rf $LOCAL_PATH/project_list_name
rm -rf $LOCAL_PATH/project_list_path
}
getNameAndPath
creatEmptyGerritProject
removeFiles
1.2 汇入AOSP源码
本步骤耗时较长,笔者实测需要6h+,请耐心等待,另外该步骤存在的坑很多,详见 2.常见问题及解决方法
注意
- 请在AOSP源码根目录执行该文件
- 请对
<>标识的内容按实际情况替换- 该脚本执行结果幂等,可以重复执行而不影响最终结果
LOCAL_PATH=`pwd`
MANIFEST_XML_FILE=$LOCAL_PATH/.repo/manifests/default.xml
USER_NAME="<Your gerrit username>"
SERVER_IP="<1.1.1.1>"
SERVER_PORT="<29418>"
function prePush()
{
git config --global http.postBuffer 4000000000
git config --global https.postBuffer 4000000000
git config --global ssh.postBuffer 4000000000
while read LINE
do
branch=`echo $LINE | grep "<superproject"`
if [ "$branch" ]
then
branch_sec=${LINE#*revision=\"}
DEFAULT_BRANCH=${branch_sec%%\"*}
echo "当前repo主分支被设置为: $DEFAULT_BRANCH"
break
fi
done < $MANIFEST_XML_FILE
}
function pushLocalToRemote()
{
# 首先向platform/manifests仓库推送文件
cd $MANIFEST
git push --no-thin ssh://$USER_NAME@$SERVER_IP:$SERVER_PORT/platform/manifests.git
cd -
# 按照xml文件汇入
while read LINE
do
cd $LOCAL_PATH
command_line=`echo $LINE | grep "<project"`
if [ "$command_line" ]
then
# echo $LINE
reposity_name_sec=${LINE#*name=\"}
reposity_path_sec=${LINE#*path=\"}
if [ "$reposity_name_sec" ] && [ "$reposity_path_sec" ]
then
reposity_name=${reposity_name_sec%%\"*}
reposity_path=${reposity_path_sec%%\"*}
src_path=$LOCAL_PATH/$reposity_path
if [ -d "$src_path" ]; then
cd $src_path
echo `pwd`
git push --no-thin ssh://$USER_NAME@$SERVER_IP:$SERVER_PORT/$reposity_name 2>&1 | tee git_push_error.log
if grep -q "Unpack error" git_push_error.log
then
echo "发现Unpack error错误,请尝试修改 gerrit.config 文件中的 receive.maxBatchCommits 字段为更大值, 并重启gerrit, 之后重新运行该脚本"
elif grep -q "unexpected disconnect" git_push_error.log
then
rm -rf git_push_error.log
echo "发现unexpected disconnect错误,请尝试修改 gerrit.config 文件中的 sshd.TCPKeepAlive = yes, 并重启gerrit, 之后重新运行该脚本"
exit 1
else
rm -rf git_push_error.log
fi
cd -
fi
fi
fi
done < $MANIFEST_XML_FILE
}
prePush
pushLocalToRemote
1.3 [附] 如果想删除所有AOSP仓库怎么办
在汇入过程中,有时候在遇到大量错误或者脚本配置有误的情况下,您有可能选择回滚所有提交并删除仓库重来,此时1100多个仓库如果靠手动删除无疑压力巨大,在此一并提供删除仓库脚本。
注意
- 该脚本将删除当前所有仓库,请务必谨慎操作
LOCAL_PATH=`pwd`
MANIFEST_XML_FILE=$LOCAL_PATH/.repo/manifests/default.xml
USER_NAME="admin"
SERVER_IP="192.168.0.100"
SERVER_PORT="29418"
OUTPUT_PROJECT_LIST_FILE_NAME=$LOCAL_PATH/project_list_name
OUTPUT_PROJECT_LIST_FILE_PATH=$LOCAL_PATH/project_list_path
function getNameAndPath()
{
echo > $OUTPUT_PROJECT_LIST_FILE_NAME
echo > $OUTPUT_PROJECT_LIST_FILE_PATH
while read LINE
do
command_line=`echo $LINE | grep "<project"`
if [ "$command_line" ]
then
#echo $LINE
reposity_name_sec=${LINE#*name=\"}
reposity_path_sec=${LINE#*path=\"}
if [ "$reposity_name_sec" ] && [ "$reposity_path_sec" ]
then
reposity_name=${reposity_name_sec%%\"*}
reposity_path=${reposity_path_sec%%\"*}
echo "$reposity_name" >> $OUTPUT_PROJECT_LIST_FILE_NAME
echo "$reposity_path" >> $OUTPUT_PROJECT_LIST_FILE_PATH
fi
fi
done < $MANIFEST_XML_FILE
}
function deleteGerritProject()
{
for i in `cat $OUTPUT_PROJECT_LIST_FILE_NAME`;
do
echo $i
echo "ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP delete-project delete --yes-really-delete $i"
ssh -p $SERVER_PORT $USER_NAME@$SERVER_IP delete-project delete --yes-really-delete $i
done
}
function removeFiles()
{
rm -rf $LOCAL_PATH/project_list_name
rm -rf $LOCAL_PATH/project_list_path
}
getNameAndPath
deleteGerritProject
removeFiles
2. 常见问题及解决方法
2.1 error: 远程解包失败:error Missing commit xxx
2.1.1 问题原因
当源码汇入gerrit过程中,部分仓库可能会有类似如下报错:
/home/xxx/Project/Android/aosp/kernel/prebuilts/4.19/arm64
枚举对象中: 15, 完成.
对象计数中: 100% (15/15), 完成.
使用 16 个线程进行压缩
压缩对象中: 100% (14/14), 完成.
写入对象中: 100% (15/15), 37.12 MiB | 10.63 MiB/s, 完成.
总共 15(差异 0),复用 0(差异 0),包复用 0
error: 远程解包失败:error Missing commit a51c5febf3fb291d9258fa41176972e55b942f3c
fatal: Unpack error, check server log
To ssh://192.168.1.1:29418/kernel/prebuilts/4.19/arm64
! [remote rejected] android-13.0.0_r52 -> android-13.0.0_r52 (n/a (unpacker error))
error: 无法推送一些引用到 'ssh://192.168.0.100:29418/kernel/prebuilts/4.19/arm64'
检索stackoverflow,发现有开发者报告过这个问题git-unpack-error-on-push-to-gerrit,根据该网站建议,我尝试使用--no-thin参数进行源码汇入,但发现该问题仍旧不能解决,经过笔者调查分析,该问题是提交时gerrit限制了maxBatchCommits参数,导致部分仓库由于commit过多被拒绝。
2024.9.21 更:感谢@蔡树伟先生给出的进一步问题分析,现将蔡先生的问题分析更新如下:
应该是部分repo源克隆项目的时候为了节省空间加快速度,使用浅克隆,所以本地git项目只有最新的提交记录,并且有他的父级commit id,这也是导致missing commit的原因。
2.2.2 解决方案
按如下示例修改 gerrit.config 文件中的 receive.maxBatchCommits 字段为更大值, 并重启gerrit, 之后重新汇入。
[receive]
enableSignedPush = false
maxBatchCommits = 999999999 # 这个地方按实际情况尽可能增大
timeout = 120min # 这个地方推荐120min
然后,再次运行原始命令。
2024.9.21 更: @蔡树伟给出该问题的解决方案:
当发生如2.1.1的问题时,如果还能访问之前的远程仓库可以参考这个文章(blog.csdn.net)取回完整的提交历史记录。
如果旧的服务器已经没法访问了,可以在git项目下执行git filter-branch -f -- --all,这个命令会重置所有分支,使得那些持有无效commit的提交作为分支的根,也就不再有父级提交了。
2.3 fatal: 远端意外挂断了
2.3.1 问题原因
当源码汇入gerrit过程中,部分仓库可能会有类似如下报错:
/home/xxx/Project/Android/aosp/prebuilts/clang-tools
枚举对象中: 274, 完成.
对象计数中: 100% (274/274), 完成.
使用 16 个线程进行压缩
压缩对象中: 100% (210/210), 完成.
Connection to 192.168.1.1 closed by remote host.s
send-pack: unexpected disconnect while reading sideband packet
fatal: 远端意外挂断了
该问题笔者在网上找到了相似问题git-push-fails-fatal-the-remote-end-hung-up-unexpectedly,但给出的解决方案未能解决该问题,最后通过分析,笔者经过对gerrit报错分析,考虑是gerrit自身sshd没有开启ssh连接保活导致的。
2.3.2 解决方案
按如下示例修改 gerrit.config 文件中的 sshd.TCPKeepAlive = yes, 并重启gerrit, 之后重新汇入。
执行如下命令:
[sshd]
listenAddress = *:29418
TCPKeepAlive = yes # 增加这个字段以启用ssh保活
参考文档
- 将AOSP完整导入Gerrit Server (笔者注:截至笔者发稿,该文档地址已不支持访问,但该文档中给出的方式方法具有重要参考价值,特此保留)
- 将安卓代码导入gerrit
- 安卓代码(repo管理)完整导入Gerrit服务器和Gerrit代码迁移备份
以上就是我将aosp源码汇入gerrit的全流程与遇到的问题,有很多大坑小坑都没能及时的记录下来,如果您在汇入过程中遇到了其他错误,也非常欢迎您在下方留言,如果我正巧遇到过,就可以为您提供一些参考,同时也可以把这篇文章的排错指南丰富一下。
