Android源码下载+编译+调试框架层代码

源码下载

概要

repo是Android为了方便管理多个git库而开发的Python脚本。repo的出现,并非为了取代git,而是为了让Android开发者更为有效的利用git。

Android源码包含数百个git库,仅仅是下载这么多git库就是一项繁重的任务,所以在下载源码时,Android就引入了repo。 Android官方推荐下载repo的方法是通过Linux curl命令,下载完后,为repo脚本添加可执行权限:

$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a+x ~/bin/repo

由于国内Google访问受限,所以上述命令不一定能下载成功。其实,我们现在可以从很多第三方渠道找到repo脚本,只需要取下来,确保repo可以正确执行即可。

工作原理

repo需要关注当前git库的数量、名称、路径等,有了这些基本信息,才能对这些git库进行操作。通过集中维护所有git库的清单,repo可以方便的从清单中获取git库的信息。 这份清单会随着版本演进升级而产生变化,同时也有一些本地的修改定制需求,所以,repo是通过一个git库来管理项目的清单文件的,这个git库名字叫manifests。

当打开repo这个可执行的python脚本后,发现代码量并不大(不超过1000行),难道仅这一个脚本就完成了AOSP数百个git库的管理吗?并非如此。 repo是一系列脚本的集合,这些脚本也是通过git库来维护的,这个git库名字叫repo。

在客户端使用repo初始化一个项目时,就会从远程把manifests和repo这两个git库拷贝到本地,但这对于Android开发人员来说,又是近乎无形的(一般通过文件管理器,是无法看到这两个git库的)。 repo将自动化的管理信息都隐藏根目录的.repo子目录中。

项目清单库(.repo/manifests)

AOSP项目清单git库下,只有一个文件default.xml,是一个标准的XML,描述了当前repo管理的所有信息。 AOSP的default.xml的文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote  name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
<default revision="master"
remote="aosp"
sync-j="4" />
<project path="build" name="platform/build" groups="pdk,tradefed" >
<copyfile src="core/root.mk" dest="Makefile" />
</project>
<project path="abi/cpp" name="platform/abi/cpp" groups="pdk" />
<project path="art" name="platform/art" groups="pdk" />
    ...
<project path="tools/studio/translation" name="platform/tools/studio/translation" groups="notdefault,tools" />
<project path="tools/swt" name="platform/tools/swt" groups="notdefault,tools" />
</manifest>

<remote>:描述了远程仓库的基本信息。name描述的是一个远程仓库的名称,通常我们看到的命名是origin;fetch用作项目名称的前缘,在构造项目仓库远程地址时使用到;review描述的是用作code review的server地址

<default>:default标签的定义的属性,将作为<project>标签的默认属性,在<project>标签中,也可以重写这些属性。属性revision表示当前的版本,也就是我们俗称的分支;属性remote描述的是默认使用的远程仓库名称,即<remote>标签中name的属性值;属性sync-j表示在同步远程代码时,并发的任务数量,配置高的机器可以将这个值调大

<project>:每一个repo管理的git库,就是对应到一个<project>标签,path描述的是项目相对于远程仓库URL的路径,同时将作为对应的git库在本地代码的路径; name用于定义项目名称,命名方式采用的是整个项目URL的相对地址。 譬如,AOSP项目的URL为https://android.googlesource....,命名为platform/build的git库,访问的URL就是https://android.googlesource....

如果需要新增或替换一些git库,可以通过修改default.xml来实现,repo会根据配置信息,自动化管理。但直接对default.xml的定制,可能会导致下一次更新项目清单时,与远程default.xml发生冲突。 因此,repo提供了一个种更为灵活的定制方式local_manifests:所有的定制是遵循default.xml规范的,文件名可以自定义,譬如local_manifest.xml, another_local_manifest.xml等, 将定制的XML放在新建的.repo/local_manifests子目录即可。repo会遍历.repo/local_manifests目录下的所有*.xml文件,最终与default.xml合并成一个总的项目清单文件manifest.xml。

local_manifests的修改示例如下:

$ ls .repo/local_manifests
local_manifest.xml
another_local_manifest.xml

$ cat .repo/local_manifests/local_manifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
    <project path="manifest" name="tools/manifest" />
    <project path="platform-manifest" name="platform/manifest" />
</manifest>

repo脚本库(.repo/repo)

repo对git命令进行了封装,提供了一套repo的命令集(包括init, sync等),所有repo管理的自动化实现也都包含在这个git库中。 在第一次初始化的时候,repo会从远程把这个git库下载到本地。

仓库目录和工作目录

仓库目录保存的是历史信息和修改记录,工作目录保存的是当前版本的信息。一般来说,一个项目的Git仓库目录(默认为.git目录)是位于工作目录下面的,但是Git支持将一个项目的Git仓库目录和工作目录分开来存放。 对于repo管理而言,既有分开存放,也有位于工作目录存放的:

manifests: 仓库目录有两份拷贝,一份位于工作目录(.repo/manifests)的.git目录下,另一份独立存放于.repo/manifests.git

repo:仓库目录位于工作目录(.repo/repo)的.git目录下

project:所有被管理git库的仓库目录都是分开存放的,位于.repo/projects目录下。同时,也会保留工作目录的.git,但里面所有的文件都是到.repo的链接。这样,即做到了分开存放,也兼容了在工作目录下的所有git命令。

既然.repo目录下保存了项目的所有信息,所有要拷贝一个项目时,只是需要拷贝这个目录就可以了。repo支持从本地已有的.repo中恢复原有的项目。

使用介绍

repo命令的使用格式如下所示:

$ repo <COMMAND> <OPTIONS>

可选的的有:help、init、sync、upload、diff、download、forall、prune、start、status,每一个命令都有实际的使用场景, 下面我们先对这些命令做一个简要的介绍:

3.1 init
$ repo init -u <URL> [<OPTIONS>]
-u:指定manifests这个远程git库的URL,manifests库是整个项目的清单。默认情况,这个git库只包含了default.xml一个文件,其内容可以参见Android的样本

-m, –manifest-name:指定所需要的manifests库中的清单文件。默认情况下,会使用maniftests/default.xml

-b, –manifest-branch:指定manifest.xml文件中的一个版本,,也就是俗称的“分支”

运行该命令后,会在当前目录下新建一个.repo子目录:

.repo
├── manifests       # 一个git库,包含default.xml文件,用于描述repo所管理的git库的信息
├── manifests.git   # manifest这个git库的实体,manifest/.git目录下的所有文件都会链接到该目录
├── manifest.xml    # manifests/default.xml的一个软链接
└── repo            # 一个git库,包含repo运行的所有脚本

这些本地的目录是如何生成的呢?执行repo命令时,可以通过--trace参数,来看实际发生了什么。

$ repo --trace init -u $URL -b $BRANCH -m $MANIFEST
  --------------------
  mkdir .repo; cd .repo
  git clone --bare $URL manifests.git
  git clone https://android.googlesource.com/tools/repo
  mkdir -p manifests/.git; cd manifests/.git
  for i in ../../manifests.git/*; do ln -s $ı .; done
  cd ..
  git checkout $BRANCH -- .
  cd ..
  ln -s manifests/$MANIFEST manifest.xml

首先,在当前目录下创建.repo子目录,后续所有的操作都在.repo子目录下完成;

然后,clone了两个git库,其中一个是-u参数指定的manifests,本地git库的名称是manifest.git;另一个是默认的repo,后面我们会看到这个URL也可以通过参数来指定;

接着,创建了manifest/.git目录,里面的所有文件都是到manifests.git这个目录的链接,这个是为了方便对manifests目录执行git命令,紧接着,就会将manifest切换到-b参数指定的分支;

最后,在.repo目录下,创建了一个软链接,链接到-m参数制定的清单文件,默认情况是manifests/default.xml。

这样,就完成了一个多git库的初始化,之后,就可以执行其他的repo命令了。

我们还介绍几个不常用的参数,在国内下载Android源码时,会用到:

–repo-url:指定远程repo库的URL,默认情况是https://android.googlesource....,但国内访问Google受限,会导致这个库无法下载,从而导致repo init失败,所以可以通过该参数指定一个访问不受限的repo地址

–repo-branch:同manifest这个git库一样,repo这个git库也是有版本差异的,可以通过该参数来指定下载repo这个远程git库的特定分支

–no-repo-verify:在下载repo库时,会对repo的源码进行检查。通过–repo-url指定第三方repo库时,可能会导致检查不通过,所以可以配套使用该参数,强制不进行检查

sync

$ repo sync [PROJECT_LIST]
下载远程代码,并将本地代码更新到最新,这个过程称为“同步”。如果不使用任何参数,那么会对所有repo管理的进行同步操作;也可以PROJECT_LIST参数,指定若干要同步的PROJECT。 根据本地git库代码不同,同步操作会有不同的行为:

当本地的git库是第一次触发同步操作时,那么,该命令等价于git clone,会将远程git库直接拷贝到本地

当本地已经触发过同步操作时,那么,该命令等价于git remote update && git rebase origin/<BRANCH>,<BRANCH>就是当前与本地分支所关联的远程分支 代码合并可能会产生冲突,当冲突出现时,只需要解决完冲突,然后执行git rebase --continue即可。

当sync命令正确执行完毕后,本地代码就同远程代码保持一致了。在一些场景下,我们会用到sync命令的一些参数:

-j:开启多线程同步操作,这会加快sync命令的执行速度。默认情况下,使用4个线程并发进行sync

-c, –current-branch:只同步指定的远程分支。默认情况下,sync会同步所有的远程分支,当远程分支比较多的时候,下载的代码量就大。使用该参数,可以缩减下载时间,节省本地磁盘空间

-d, –detach:脱离当前的本地分支,切换到manifest.xml中设定的分支。在实际操作中,这个参数很有用,当我们第一次sync完代码后,往往会切换到dev分支进行开发。如果不带该参数使用sync, 则会触发本地的dev分支与manifest设定的远程分支进行合并,这会很可能会导致sync失败

-f, –force-broken:当有git库sync失败了,不中断整个同步操作,继续同步其他的git库

–no-clone-bundle:在向服务器发起请求时,为了做到尽快的响应速度,会用到内容分发网络(CDN, Content Delivery Network)。同步操作也会通过CDN与就近的服务器建立连接, 使用HTTP/HTTPS的$URL/clone.bundle来初始化本地的git库,clone.bundle实际上是远程git库的镜像,通过HTTP直接下载,这会更好的利用网络带宽,加快下载速度。 当服务器不能正常响应下载$URL/clone.bundle,但git又能正常工作时,可以通过该参数,配置不下载$URL/clone.bundle,而是直接通过git下载远程git库

upload

$ repo upload [PROJECT_LIST]
从字面意思理解,upload就是要上传,将本地的代码上传到远程服务器。upload命令首先会找出本地分支从上一次同步操作以来发生的改动,然后会将这些改动生成Patch文件,上传至Gerrit服务器。 如果没有指定PROJECT_LIST,那么upload会找出所有git库的改动;如果某个git库有多个分支,upload会提供一个交互界面,提示选择其中若干个分支进行上传操作。

upload并不会直接将改动合并后远程的git库,而是需要先得到Reviewer批准。Reviewer查看改动内容、决定是否批准合入代码的操作,都是通过Gerrit完成。 Gerrit服务器的地址是在manifests中指定的:打开.repo/manifest.xml,<remote>这个XML TAG中的review属性值就是Review服务器的URL:

<remote name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
Gerrit的实现机制不是本文讨论的内容,但有几个与Gerrit相关的概念,是需要代码提交人员了解的:

Reviewer:代码审阅人员可以是多个,是需要人为指定的。Gerrit提供网页的操作,可以填选Reviewer。当有多个git库的改动提交时,为了避免在网页上频繁的填选Reviewer这种重复劳动, upload提供了–re, –reviewer参数,在命令行一次性指定Reviewer

Commit-ID:git为了标识每个提交,引入了Commit-ID,是一个SHA-1值,针对当次提交内容的一个Checksum,可以用于验证提交内容的完整性

Change-ID:Gerrit针对每一个Review任务,引入了一个Change-ID,每一个提交上传到Gerrit,都会对应到一个Change-ID, 为了区分于Commit-ID,Gerrit设定Change-ID都是以大写字母 “I” 打头的。 Change-ID与Commit-ID并非一一对应的,每一个Commit-ID都会关联到一个Change-ID,但Change-ID可以关联到多个Commit-ID

Patch-Set:当前需要Review的改动内容。一个Change-ID关联多个Commit-ID,就是通过Patch-Set来表现的,当通过git commit --amend命令修正上一次的提交并上传时, Commit-ID已经发生了变化,但仍可以保持Change-ID不变,这样,在Gerrit原来的Review任务下,就会出现新的Patch-Set。修正多少次,就会出现多少个Patch-Set, 可以理解,只有最后一次修正才是我们想要的结果,所以,在所有的Patch-Set中,只有最新的一个是真正有用的,能够合并的。

download

$ repo download <TARGET> <CHANGE>
upload是把改动内容提交到Gerrit,download是从Gerrit下载改动。与upload一样,download命令也是配合Gerrit使用的。

<TARGET>:指定要下载的PROJECT,譬如platform/frameworks/base, platform/packages/apps/Mms

<CHANGE>:指定要下载的改动内容。这个值不是Commit-ID,也不是Change-ID,而是一个Review任务URL的最后几位数字。 譬如,AOSP的一个Review任务https://android-review.google...,其中23823就是<CHANGE>。

forall

$ repo forall [PROJECT_LIST] -c <COMMAND>
对指定的git库执行-c参数制定的命令序列。在管理多个git库时,这是一条非常实用的命令。PROJECT_LIST是以空格区分的,譬如:

$ repo forall frameworks/base packages/apps/Mms -c "git status"
表示对platform/frameworks/base和platform/packages/apps/Mms同时执行git status命令。 如果没有指定PROJECT_LIST,那么,会对repo管理的所有git库都同时执行命令。

该命令的还有一些其他参数:

-r, –regex: 通过指定一个正则表达式,只有匹配的PROJECT,才会执行指定的命令

-p:输出结果中,打印PROJECT的名称

prune

$ repo prune [<PROJECT_LIST>]
删除指定PROJECT中,已经合并的分支。当在开发分支上代码已经合并到主干分支后,使用该命令就可以删除这个开发分支。

随着时间的演进,开发分支会越来越多,在多人开发同一个git库,多开发分支的情况会愈发明显,假设当前git库有如下分支:

* master
  dev_feature1_201501   # 已经合并到master
  dev_feature2_201502   # 已经合并到master
  dev_feature3_201503   # 正在开发中,还有改动记录没有合并到master

那么,针对该git库使用prune命令,会删除dev_feature1_201501和dev_feature2_201502。

定义删除无用的分支,能够提交团队的开发和管理效率。prune就是删除无用分支的”杀手锏“。

start

$ repo start <BRANCH_NAME> [<PROJECT_LIST>]
在指定的PROJECT的上,切换到<BRANCH_NAME>指定的分支。可以使用–all参数对所有的PROJECT都执行分支切换操作。 该命令实际上是对git checkout命令的封装,<BRANCH_NAME>是自定义的,它将追踪manifest中指定的分支名。

当第一次sync完代码后,可以通过start命令将git库切换到开发分支,避免在匿名分支上工作导致丢失改动内容的情况。

status

$ repo status [<PROJECT_LIST>]
status用于查看多个git库的状态。实际上,是对git status命令的封装。

使用实践

Android推荐的开发流程是:

repo init初始化工程,指定待下载的分支
repo sync下载代码
repo start将本地git库切换到开发分支(TOPIC BRANCH)

在本地进行修改,验证后,提交到本地
repo upload上传到服务器,等待review
在实际使用过程中,我们会用到repo的一些什么子命令和参数呢?哪些参数有助于提高开发效率呢?下面我们以一些实际场景为例展开说明。

对项目清单文件进行定制

通过local_manifest机制,能够避免了直接修改default.xml,不会造成下次同步远程清单文件的冲突。

CyanogenMod(CM)适配了上百款机型,不同机型所涉及到的git库很可能是有差异的。以CM对清单文件的定制为例,通过新增local_manifest.xml,内容如下:

<manifest>
<!-- add github as a remote source -->
<remote name="github" fetch="git://github.com" />
 
<!-- remove aosp standard projects and replace with cyanogenmod versions -->
<remove-project name="platform/bootable/recovery" />
<remove-project name="platform/external/yaffs2" />
<remove-project name="platform/external/zlib" />
<project path="bootable/recovery" name="CyanogenMod/android_bootable_recovery" remote="github" revision="cm-10.1" />
<project path="external/yaffs2" name="CyanogenMod/android_external_yaffs2" remote="github" revision="cm-10.1" />
<project path="external/zlib" name="CyanogenMod/android_external_zlib" remote="github" revision="cm-10.1" />
 
<!-- add busybox from the cyanogenmod repository -->
<project path="external/busybox" name="CyanogenMod/android_external_busybox" remote="github" revision="cm-10.1" />
 
</manifest>

local_manifest.xml会与已有的default.xml融合成一个项目清单文件manifest.xml,实现了对一些git库的替换和新增。 可以通过以下命令导出当前的清单文件,最终snapshot.xml就是融合后的版本:

$ repo manifest -o snapshot.xml -r

在编译之前,保存整个项目的清单,有助于问题的回溯。当项目的git库发生变更,需要回退到上一个版本进行验证的时候,只需要重新基于snapshot.xml初始化上一个版本即可:

$ cp snapshot.xml .repo/manifests/
$ repo init -m snapshot.xml           # -m 参数表示自定义manifest
$ repo sync -d                        # -d 参数表示从当前分支脱离,切换到manifest中定义的分支

解决无法下载Android源码

在repo init的时候,会从远程下载manifests和repo这两个git库,默认情况下,这两个git库的地址都是写死在repo这个python脚本里面的。对于AOSP而言,这两个git库的地址显然是google提供的。 但由于google访问受限的缘故,会导致init时,无法下载manifests和repo。这时候,可以使用init的-u和–repo-url参数,自定义这两个库的地址,辅以–no-repo-verify来绕过代码检查。

$ repo init --repo-url [PATH/TO/REPO] -u [PATH/TO/MANIFEST] -b [BRANCH] --no-repo-verify
$ repo sync

更快更省的下载远程代码

repo默认会同步git库的所有远程分支的代码,但实际开发过程中,用到的分支是有限的。使用sync的-c参数,可以只下载manifest中设定的分支,这会节省代码下载时间以及本地的磁盘空间:

$ repo sync -c

如果实际开发过程中,需要用到另外一个分支,而又不想被其他分支干扰,可以在已有的工程根目录下,使用如下命令:

$ repo manifest -o snapshot.xml -r
$ repo init -u [PATH/TO/MANIFEST] -b [ANOTHER_BRANCH]
$ repo sync -c -d

以上命令序列,相当更新了manifest,而且仅仅只下载ANOTHER_BRANCH的代码,这样本地只保存了两个分支的代码。利用保存的snapshot.xml,还能将所有git库方便的切换回原来的分支。

如果本地已经有一份Android源码,假设路径为~/android-exsit,想要下载另一份新的Android源码,通过–reference参数,在数分钟以内,就能将代码下载完毕:

$ mkdir ~/android-new && cd ~/android-new
$ repo init --reference=~/android-exsit -u [PATH/TO/MANIFEST] -b [BRANCH]
$ repo sync -c

避免在匿名分支上工作

在sync完代码后,所有git库默认都是在一个匿名分支上(no branch),很容易会由于误操作导致丢失代码修改。可以使用如下命令将所有的git库切换到开发分支:

$ repo start BRANCH --all

使用upload提交代码

开发人员可能同时在多个git库,甚至多个分支上,同时进行修改,针对每个git库单独提交代码是繁琐的。可以使用如下命令,一并提交所有的修改:

$ repo upload

不用担心会漏提交或者误提交,upload会提供一个交互界面,开发人员选择需要提交的git库和分支即可。

如果需要省去Gerrit上填写reviewer的操作,可以使用–reviewer参数指定Reviewer的邮箱地址:

$ repo upload --reviewer="R.E.viewer@google.com"

定期删除已经合并的开发分支

Git鼓励在修复Bug或者开发新的Feature时,都创建一个新的分支。创建Git分支的代价是很小的,而且速度很快,因此,不用担心创建Git分支的成本,而是尽可能多地使用分支。

随着时间的演进,开发分支会越来越多,而一些已经合并到主干的开发分支是没有存在价值的,可以通过prune命令定期删除无用的开发分支:

$ repo prune [PROJECT_LIST]

同时操作多个git库

对于部分开发人员而言,同时操作多个git库是常态,如果针对每个git库的操作命令都是相同的,那么可以使用如下命令一次性完成所有操作:

$ repo forall -c "git branch | grep tmp | xargs git branch -D; git branch"

参数-c指定的命令序列可以很复杂,多条命令只需要用“;”间隔。

下载源代码

google官网教程:

https://source.android.com/source/downloading.html

Android 源代码树位于由 Google 托管的 Git 代码库中。Git 代码库中包含 Android 源代码的元数据,其中包括与对源代码进行的更改以及更改日期相关的元数据。本文档介绍了如何下载特定 Android 代码流水线的源代码树。

要从特定设备的出厂映像开始,请参阅选择设备版本。
https://source.android.com/so...

安装 Repo

Repo 是一款工具,可让您在 Android 环境中更轻松地使用 Git。

mkdir ~/bin
PATH=~/bin:$PATH

下载 Repo 工具,并确保它可执行:

curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

初始化 Repo 客户端,创建一个空目录来存放您的工作文件。

mkdir WORKING_DIRECTORY
cd WORKING_DIRECTORY

使用有效的电子邮件配置git仓库:

git config --global user.name "Your Name"
git config --global user.email "you@example.com"

运行 repo init 以获取最新版本的 Repo 及其最近的所有错误更正内容.

repo init -u https://android.googlesource.com/platform/manifest

要对“master”以外的分支进行校验,请使用 -b 来指定相应分支。要查看分支列表,请参阅源代码标记和版本。

repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1

初始化成功之后,会在当前目录下生成一个.repo的文件夹:

下载 Android 源代码树

要将 Android 源代码树从默认清单中指定的代码库下载到工作目录,请运行以下命令:

repo sync

Android 源代码文件将位于工作目录中对应的项目名称下。

排查网络问题

在使用代理的情况下下载内容(在一些企业环境中很常见)时,您可能需要明确指定 Repo 随后使用的代理:

export HTTP_PROXY=http://<proxy_user_id>:<proxy_password>@<proxy_server>:<proxy_port>
export HTTPS_PROXY=http://<proxy_user_id>:<proxy_password>@<proxy_server>:<proxy_port>

sudo sysctl -w net.ipv4.tcp_window_scaling=0
repo sync -j1

使用本地镜像

当您使用多个客户端时(尤其是在带宽不足的情况下),最好为所有服务器内容创建一个本地镜像,并从该镜像同步客户端(不需要访问网络)。一个完整镜像的下载文件比两个客户端的下载文件要小一些,而且包含更多信息。

以下说明假定在 /usr/local/aosp/mirror 中创建镜像。第一步是创建并同步镜像本身。请注意 --mirror 标志,该标志只能在创建新客户端时指定:

mkdir -p /usr/local/aosp/mirror
cd /usr/local/aosp/mirror
repo init -u https://android.googlesource.com/mirror/manifest --mirror
repo sync

同步镜像后,您就可以从镜像创建新客户端了。请注意,务必要指定一个绝对路径:

mkdir -p /usr/local/aosp/master
cd /usr/local/aosp/master
repo init -u /usr/local/aosp/mirror/platform/manifest.git
repo sync

最后,要将客户端与服务器同步,您需要将镜像与服务器同步,然后再将客户端与镜像同步:

cd /usr/local/aosp/mirror
repo sync
cd /usr/local/aosp/master
repo sync

您可以将镜像存储在 LAN 服务器上,然后通过 NFS、SSH 或 Git 访问它。您还可以将其存储在移动存储盘上,并在用户之间或计算机之间传用该存储盘。

开发

要处理 Android 代码,您需要同时使用 Git 和 Repo。在大多数情况下,您可以仅使用 Git(不必使用 Repo),或结合使用 Repo 和 Git 命令以组成复杂的命令。不过,使用 Repo 执行基本的跨网络操作可大大简化您的工作。

开发工具:git、Repo、Gerrit、Android Studio

Git 是一个开放源代码的版本控制系统,专用于处理分布在多个代码库上的大型项目。在 Android 环境中,我们会使用 Git 执行本地操作,例如建立本地分支、提交、查看更改、修改。打造 Android 项目所面临的挑战之一就是确定如何最好地支持外部社区 - 从业余爱好者社区到生产大众消费类设备的大型原始设备制造商 (OEM)。我们希望组件可以替换,并希望有趣的组件能够在 Android 之外自行发展。我们最初决定使用一种分布式修订版本控制系统,经过筛选,最后选中了 Git。

Repo 是我们以 Git 为基础构建的代码库管理工具。Repo 可以在必要时整合多个 Git 代码库,将相关内容上传到我们的修订版本控制系统,并自动执行 Android 开发工作流程的部分环节。Repo 并非用来取代 Git,只是为了让您在 Android 环境中更轻松地使用 Git。Repo 命令是一段可执行的 Python 脚本,您可以将其放在路径中的任何位置。使用 Android 源代码文件时,您可以使用 Repo 执行跨网络操作。例如,您可以借助单个 Repo 命令,将文件从多个代码库下载到本地工作目录。

Gerrit 是一个基于网页的代码审核系统,适用于使用 Git 的项目。Gerrit 允许所有授权用户提交更改(如果通过代码审核,这些更改会自动纳入项目中),以此鼓励他们更集中地使用 Git。此外,Gerrit 可以在浏览器中并排显示更改,并支持代码内注释,使得审核工作变得更轻松。

Android Studio 是用于开发 Android 应用的官方集成开发环境 (IDE)。如需了解详情,请参阅 Android Studio 概览。

基本工作流程基本工作流程示意图
图 1. Android 基本工作流程
https://source.android.com/im...
Android源码下载+编译+调试框架层代码

与代码库进行交互的基本模式如下:

使用 repo start 新建一个主题分支。

修改文件。

使用 git add 暂存更改。

使用 git commit 提交更改。

使用 repo upload 将更改上传到审核服务器。

任务参考

以下任务列表简要总结了如何执行常见的 Repo 和 Git 任务。要了解如何使用 Repo 下载源代码,请参阅下载源代码和使用 Repo。

同步客户端

要同步所有可用项目的文件,请运行以下命令:

repo sync

要同步所选项目的文件,请运行以下命令:

repo sync PROJECT0 PROJECT1 ... PROJECTN

创建主题分支

当您开始进行更改(例如当您开始处理错误或使用新功能)时,请在本地工作环境中新建一个主题分支。主题分支不是原始文件的副本;它代表着特定提交。这样一来,您可以轻松创建本地分支并在这些分支之间切换。通过使用分支,您可以将工作的某个方面与其他方面分隔开来。请参阅分隔主题分支(一篇有关使用主题分支的趣味文章)。

要使用 Repo 新建一个主题分支,请转到要修改的项目并运行以下命令:

repo start BRANCH_NAME .

请注意,句点代表当前工作目录中的项目。要验证您的新分支是否已创建,请运行以下命令:

repo status .

使用主题分支
要将分支分配给特定项目,请运行以下命令:

repo start BRANCH_NAME PROJECT_NAME

要查看所有项目的列表,请访问 android.googlesource.com。再次提醒,如果您已转到特定的项目目录,可以简单地使用一个句点来表示当前项目。

要切换到您已在本地工作环境中创建的另一个分支,请运行以下命令:

git checkout BRANCH_NAME

要查看现有分支的列表,请运行以下命令:

git branch

repo branches

当前分支的名称前面将标注星号。

注意:如果存在错误,可能会导致 repo sync 重置本地主题分支。如果在您运行 repo sync 之后,git branch 显示 *(无分支),请再次运行 git checkout。

暂存文件
默认情况下,Git 会检测到您在项目中所做的更改,但不会跟踪这些更改。要让 Git 保存您的更改,您必须将更改标记为包含在提交中。这也称为“暂存”。

您可以通过运行以下命令来暂存更改:

git add

对于此命令,项目目录中的任何文件或目录都可作为参数。git add 并不像其名称表示的这样只是简单地将文件添加到 Git 代码库,它还可以用于暂存文件的修改和删除的内容。

查看客户端状态
要列出文件的状态,请运行以下命令:

repo status

要查看未提交的修改,请运行以下命令:

repo diff

如果您准备立即提交,运行 repo diff 命令可让系统显示您所做的不会被提交的每一项本地修改。如果您准备立即提交,要查看将被提交的每一项修改,则需要运行 Git 命令 git diff。在运行该命令之前,请确保您已转到项目目录下:

cd ~/WORKING_DIRECTORY/PROJECT
git diff --cached

提交更改
在 Git 中,提交是修订版本控制的基本单位,包含目录结构的快照以及整个项目的文件内容。在 Git 中创建提交很简单,只需输入以下命令即可:

git commit

系统会提示您使用惯用的编辑器提供一条提交消息;请为您提交到 AOSP 的所有更改都提供一条会有帮助作用的消息。如果您没有添加日志消息,提交将会终止。

将更改上传到 Gerrit
上传之前,请先更新为最新修订版本:

repo sync

然后运行以下命令:

repo upload

运行此命令后,系统会随即列出您已提交的更改,并提示您选择要上传到审核服务器的分支。如果只有一个分支,您会看到一个简单的 y/n 提示符。

恢复同步冲突
如果 repo sync 显示同步冲突,请执行以下操作:

查看未合并的文件(状态代码 = U)。
根据需要修改存在冲突的地方。
在相关项目目录中进行更改,为相关文件运行 git add 和 git commit,然后对这些更改执行“衍合”(rebase) 命令。例如:

git add .
git commit
git rebase --continue

当衍合完成后,再一次开始整个同步过程:

repo sync PROJECT0 PROJECT1 ... PROJECTN

清理您的客户端文件
要在更改合并到 Gerrit 中后更新您的本地工作目录,请运行以下命令:

repo sync

要安全移除已过时的主题分支,请运行以下命令:

repo prune

删除客户端
由于所有状态信息都会存储在客户端中,您只需从文件系统中删除相应目录即可:

rm -rf WORKING_DIRECTORY

删除客户端将永久删除您尚未上传以供审核的任何更改。

Git 和 Repo 快速参考表基本 Git 和 Repo 命令列表
图 2. 基本 Git 和 Repo 命令
https://source.android.com/im...
Android源码下载+编译+调试框架层代码

相关推荐