本文需要有对git repo gerrit的基本使用,
这里不提及过多的基本用法.
00. Books
- ProGit/ProGit 2nd https://git-scm.com/book/zh/v2
建议至少把 第2章 git基础 看一遍
- Git权威指南 http://www.worldhello.net/gotgit/
建议仅看repo gerrit相关的章节
Android式多版本库协同
Gerrit代码审核服务器
01. Repo 的产生
Android版本库众多的原因,主要原因是版本库太大以及Git不能部分检出。
如果所有的东西都放在一个库中,而某个开发团队感兴趣的可能就是某个驱动,
或者是某个应用,却要下载如此庞大的版本库,是有些说不过去。
git也有submodule供多个库下载,但这功能使用不方便,
其局限性和麻烦可参看
http://www.worldhello.net/got…
如果是你,有什么方案来管理这么多的库?
Repo是Google开发的用于管理Android版本库的一个工具。
Repo并不是用于取代Git,是用Python对Git进行了一定的封装,简化了对多个Git版本库的管理。
对于repo管理的任何一个版本库,都还是需要使用Git命令进行操作
02. Repo 如何组织这么多库 –manifest文件?
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="aosp"
fetch=".." />
<default revision="refs/tags/android-7.1.0_r4"
remote="aosp"
sync-j="4" />
<project path="build" name="platform/build" groups="pdk,tradefed" >
<copyfile src="core/root.mk" dest="Makefile" />
</project>
<project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
......
从上看出,repo用清单文件来管理, 其内容为版本的地址,默认分支名,远程库和本地路径对应关系。
一个project对应一个库,path为本地切出来的工作目录的路径,也就是我们看到的代码路径,
name为对应的远程git库的名字/路径。
注意,一般说来,一套安卓代码都由好几百个库组成,上面只截取了一部分。
关于manifests更多的信息可查看你代码根目录的
.repo/repo/docs/manifest-format.txt
这里只说下revision,
- 你可以在project里通过revision指定与清单文件default里不一样的分支,
revision的值可能是分支/tag/commitID等形式
- 你也可以用repo manifest -r命令生成带revision信息的清单文件,可用于代码发布
生成的一个例子:
<project name="device/qcom/common" revision="3a83da1dff148dd709caac602693d3295bd0a18b" upstream="refs/heads/省略">
03. repo init 在干什么?
$repo init --help
Usage: repo init [options]
Options:
...... Manifest options:
-u URL, --manifest-url=URL
manifest repository location
-b REVISION, --manifest-branch=REVISION
manifest branch or revision
-m NAME.xml, --manifest-name=NAME.xml
initial manifest file
--mirror create a replica of the remote repositories rather
than a client working directory
--reference=DIR location of mirror directory
--depth=DEPTH create a shallow clone with given depth; see git clone
...... repo Version options:
--repo-url=URL repo repository location
--repo-branch=REVISION
repo init用法如上, 我们一般下载code的方式为
repo init -u xxx -b yyy -m zzz.xml
repo sync -c
注意
- -m 当有多个清单文件,可以指定清单库的某个清单文件为有效的清单文件
repo init命令执行后主要干了两件事
- 下载repo到.repo/repo里
- 下载并检出-u所指定的manifest库,并建立.repo/manifest.xml链接
对于第一步大家可能比较疑问,不是已经有个repo了吗?为什么还在下载repo?
实际上,我们执行的repo命令只是相当于个当函数,真正的执行命令(如repo sync)
都是.repo/repo/subcmds里的,
该子命令都用python写的, 所以当有需要,或者执行出错时想查看源码的话可在此目录下查看.
04. –mirror –reference作用
让我们想个问题,假设你一套代码有100G,
- 你们是异地协作协作,如需要上海/北京两地办公,主服务器在北京,网络很慢,
如何为上海的同事减少下代码时间? 如何缓解主服务器的压力? - 你们都在一个地办公,多人公用一个服务器, 如何节省下载代码时间? 节省服务器空间?
- 你作为SCM,每天都要完全clean编译版本, 如何更快的构建?
当然我们这只讲repo本身提供的方法,
- –mirror 建立和上游Android的版本库一模一样的镜像
–reference 通过引用已下载的mirror或者代码加快下载速度,常用使用场景:
- DailyBuild
- 一套代码兼容多个机型
- 一套代码需要多个副本
- 多个人共用服务器
具体操作为
先通过
repo init .... --mirror
建立一个本地的镜像,
然后下载代码时引用这个镜像
repo init .... --reference=镜像路径
repo sync -c
如果此时主服务器库容量增加到120G,而你的镜像没更新,
那么理论上新下载代码只需要下载20G的数据,
N套代码占用的空间为100G(镜像的) + 20G*N (新代码)
大家可用
du -sh .repo/
命令统计.
注意:
不用担心你用reference下的代码和主服务器的不同步,
git会自动进行三方对比的,保证能获取到-u 指定的地址里的代码是最新的.
05. Gerrit Change-Id
graph TD;
title(repo gerrit简单工作流程);
init(repo init/sync) --> start(repo start xxx --all);
start --> gitmodify(单个库的修改和git流程一样);
gitmodify --> upload(上传到Gerrit repo upload/git push);
upload --> review(审核 +1 +2);
review-- 通过 --> submit(Gerrit submit);
review-- 不通过 --> gitmodify;
Change-Id由git commit时调用.git/hooks/commit-msg钩子生成的,
gerrit靠该id来区分同一分支下是否为同一个提交,
事实上,只要gerrit未submit,可以在本地git commit XX –amend修正提交后,
可以再次提交,成为新的patchset,但提交号不变.
06. repo upload时gerrit帐号名与邮箱前缀不一致
repo upload时默认是用配置的邮箱前缀做为push的用户名,
如果您的邮箱前缀和gerrit账户名不一致,需要做如下配置
$ cat ~/.gitconfig
[review "您的地址"]
username = 您的帐号名
07. Gerrit command
该功能可能SCM用得多,用于实现批处理脚本.
此处只看下用法
$ ssh -p 端口号 用户名@地址 gerrit
Available commands of gerrit are:
apropos Search in Gerrit documentation
ban-commit Ban a commit from a project's repository
create-account Create a new batch/role account
create-branch Create a new branch
create-group Create a new account group
create-project Create a new project and associated Git repository
flush-caches Flush some/all server caches from memory
gc Run Git garbage collection
gsql Administrative interface to active database
ls-groups List groups visible to the caller
ls-members List the members of a given group
ls-projects List projects visible to the caller
ls-user-refs List refs visible to a specific user
plugin
query Query the change database
receive-pack Standard Git server side command for client side git push
rename-group Rename an account group
review Verify, approve and/or submit one or more patch sets
set-account Change an account's settings
set-members Modify members of specific group or number of groups
set-project Change a project's settings
set-project-parent Change the project permissions are inherited from
set-reviewers Add or remove reviewers on a change
show-caches Display current cache statistics
show-connections Display active client SSH connections
show-queue Display the background work queues
stream-events Monitor events occurring in real time
test-submit
version Display gerrit version
See 'gerrit COMMAND --help' for more information.
08. cherry-pick rebase
这个可看下git书箱学习下,工作中也用得多,提高效率.
09. repo sync (-n -l)
-n -l参数解释如下:
-l, --local-only only update working tree, don't fetch
-n, --network-only fetch only, don't update working tree
其实repo sync = repo sync -n + repo sync -l
如果你只想获取更新到.repo库里, 不更新本地代码, 可加上 -n参数
如果你之前已经获取了更新,只有一两个库更新出错,
而你重新repo sync -c一次都得半小时,
那可以先repo sync -l, 然后单独的更新出错的库.
我一般更新都用命令
repo sync -c -f
repo sync -c -l
备注:
再运行次加-l主要是更新时可能信息很多,中间有些出错,
而我有不想慢慢看终端找出错信息,所以干脆再让他更新下
本地工作目录,这样方便查找出错信息.
10. git pushInsteadOf
试想一下这样一个场景, 您的代码下载自A服务器, 现在要往B服务器push代码,该咋办?
当然, 您可用git remote添加远程信息,然后再用git push 新添加的远程名 …
我这里讲另一个方法,就是pushInsteadOf,
eg:
vi ~/.gitconfig
[url "testB_address"]
pushInsteadOf = testA_address
即,我们仅需要在gitconfig里配置一下,
push的时候用B服务器的地址,替换A服务器的地址,
这样我们push的时候,代码就提交到B服务器了.
同理,可用
pullInsteadOf 替换 pull时的地址
InsteadOfpush 替换 push和pull时的地址
BTW,对 ~/.gitconfig文件的修改,您也可用命令
eg:
git config --global url.testB_address.insteadOf testA_address
11. Debug
有时候有些运行命令出错了,可加–trace参数查看更多信息
(git的调试方法请查看progit一书)
eg:
repo --trace sync -c
12. 建议
新下载代码后,分支处于no branch状态,建议下载代码后用
repo start 本地分支名 --all
建个分支,该分支名为本地分支名,可任意取名
该分支仅用于和服务同步,更新代码,要修改的话用repo start xx建立个新分支
不然你本地在修改,服务器也修改,会造成历史记录很乱,
有时导致编译出来的代码有啥隐含bug.
当然,您也可用git pull --rebase