Repo Gerrit进阶

本文需要有对git repo gerrit的基本使用,
这里不提及过多的基本用法.

00. Books

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

相关推荐