现在很多企业的网络一般都比较快, 但是有的企业却会限速, 如果需要从github和google code上面git clone大的仓库的话, 那么需要耗费的时间是很客观的, 例如从github或者google code, 或者其他托管服务站点获取Android中需要的多个Kernel仓库, 一般一个kernel仓库都有几GB, 如果是100KB/S的话, 那么将需要很长的时间.
与此同时, 不同的Android 版本(AOSP)代码, 他们一般都会依赖许多相同的组件, 甚至获取相同的仓库代码, 仅仅只是branch或者tag不同而已, 例如对于Nexus 7 flo平板而言, 不管是AOSP 4.4 Kitkat还是 5.X Lolipop, 都会去下载flo-kernel这个内核, 他们都remote都是一样的, 唯一不同的是tag使用的不同, 因此如果我们已经获取过Kitkat的代码,那么就可以复用其中的bare repo, 从而达到快速clone.
要了解如何做, 我们需要对AOSP的代码结构非常熟悉, 一般而言, 如果是系统工程师,那么几乎对AOSP的每一个目录都会很熟悉, 对自己需要编译的target的依赖的每一个repo都几乎会心中有数(例如external中的哪些, vendor, device都会用到哪些), 这种情况下, 就可以删除某些仓库的下载, 从而节省时间.
总结起来, 要节省git clone的时间就是从两个方面入手:
在实现前面的两点之前, 除了对AOSP的编译, 以及Target的依赖很熟悉外, 我们还需要对google 的 repo工具以及其流程有个基本的熟悉和了解.
repo的执行过程
具体查看repo这个python脚本的源码.
下面使用具体例子来讲解.
repo init -u https://github.com/bluez-android/aosp_platform_manifest.git -b lollipop
def _Checkout(cwd, branch, rev, quiet):
"""Checkout an upstream branch into the repository and track it.
"""
cmd = [GIT, 'update-ref', 'refs/heads/default', rev]
if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
raise CloneFailure()
_SetConfig(cwd, 'branch.default.remote', 'origin')
_SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
cmd = [GIT, 'symbolic-ref', 'HEAD', 'refs/heads/default']
if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
raise CloneFailure()
cmd = [GIT, 'read-tree', '--reset', '-u']
if not quiet:
cmd.append('-v')
cmd.append('HEAD')
if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
raise CloneFailure()
例如前面的 init 执行完成后, 我们可以看到在.repo目录下有一个manifest.git目录:
$ cd .repo/manifests.git/
$ git remote -v
origin https://github.com/bluez-android/aosp_platform_manifest.git (fetch)
origin https://github.com/bluez-android/aosp_platform_manifest.git (push)
.repo/manifests
$ git status
On branch default
Your branch is up-to-date with 'origin/lollipop'. // branch -b指定后在这里
cd /Dir/Contain/.repo/../
$ ls -l
total 12
drwxrwxr-x 3 hexiongjun hexiongjun 4096 Feb 15 10:57 manifests // 从下面这个bare repo checkout
drwxrwxr-x 10 hexiongjun hexiongjun 4096 Feb 15 10:54 manifests.git //bare repo
lrwxrwxrwx 1 hexiongjun hexiongjun 21 Feb 15 10:40 manifest.xml -> manifests/default.xml
drwxrwxr-x 7 hexiongjun hexiongjun 4096 Feb 15 10:40 repo // repo本身的working tree checkout
在repo init执行的时候会到: https://gerrit.googlesource.com/git-repo
checkout最新的repo, checkout下来后放在了.repo/repo目录
前面的ls -l命令中列出了manifest.xml指向的是manifests/default.xml, 然后这个xml中会指明各种fetch的url, 版本revision, 以及需要checkout下来的projects, 例如:
.............
里面的remote就是git remote获取得到的remote name, fetch指定的是从哪里checkout projects, 随后指定了revision, 以及sync的并心数.
接下来是projects的list, path指定checkout下来后放到哪里, 例如下面这行:
指定了从fetch/name这个位置clone, 因为repo本身会将所有的projects放到.repo/projects目录下面, 这个存放的位置就是由后面的groups来指定的, 例如上面的flounder-kernel的repo 本地bare repo位于:
.repo/projects/device/flounder-kernel.git
而这个repository的objects则位于:
.repo/project-objects/aosp_device_asus_flo-kernel.git/
这个objects目录大小为:
$ du -sh ../../project-objects/aosp_device_asus_flo-kernel.git/
1.2G ../../project-objects/aosp_device_asus_flo-kernel.git/
然后repo在为每一个project建立了bare repository之后, 会从本地使用git clone一份出来, 且版本为前面指定的revision.
最后xml还可以使用include来包含, 实现"重载"
了解了repo的工作过程后,我们就可以想办法来重用以前的bare repository了, 也知道如何不去clone和建立不需要的project的bare repository.
假设我们已经有了一个AOSP 4.4 Kitkat的flo-kernel的bare repository了, 而AOSP 5.1 Lollipop也需要fetch 这个project,那么我们就可以按照下面这个方式来做.
1. 拷贝project objects到对应目录
2. 拷贝projects下的对应目录到新的需要checkout的目录, 例如从AOSP4到5
然后直接repo sync, sync完成后, 我们可以看到对应的branch被重新checkout下来了:
$ git branch -a
remotes/github/kitkat
remotes/github/lollipop
remotes/m/kitkat -> github/kitkat
remotes/m/lollipop -> github/lollipop
可以看到既有kitkat也有lollipop, 这个和我们期望的一致:
cd ../device/asus/flo-kernel/
$ git branch -a
* (no branch)
remotes/github/kitkat
remotes/github/lollipop
remotes/m/kitkat -> github/kitkat
remotes/m/lollipop -> github/lollipop
例如我们使用的Nexus 7 flo来做开发, 并不需要Nexus 9等其他的内核, 那么可以直接在xml中注释掉其他的, repo就不会去下载了:
如果后来又需要checkout这些仓库,那么取消注释, 然后重新repo sync即可.
也可以使用remove-project指定, 如同上图中的前面几行.
其他的例如drawn, MIPS等一些用不到的仓库也可以删除掉.