找回密码
 注册
关于网站域名变更的通知
查看: 307|回复: 2
打印 上一主题 下一主题

Repo:Android 源码管理的利器--工作原理与常用命令全解析

[复制链接]

该用户从未签到

跳转到指定楼层
1#
 楼主| 发表于 2024-10-28 14:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
本帖最后由 Heaven_1 于 2024-10-28 16:28 编辑
& ^) q  b* H1 Z5 W! X/ e8 J- C" N! O# i7 _/ Q! v
1. 概要

+ J' k) c' Q# D
repo是Android为了方便管理多个git库而开发的Python脚本。repo的出现,并非为了取代git,而是为了让Android开发者更为有效的利用git。
Android源码包含数百个git库,仅仅是下载这么多git库就是一项繁重的任务,所以在下载源码时,Android就引入了repo。 Android官方推荐下载repo的方法是通过Linux curl命令,下载完后,为repo脚本添加可执行权限:
  1. $ curlhttps://storage.googleapis.com/git-repo-downloads/repo > , {& p4 h8 j$ o* r% i& H* d
  2. ~/bin/repo
    0 A3 Z6 j3 G4 z( B: s) B9 s
  3. $ chmod a+x~/bin/repo
复制代码
9 m4 V" ?6 k* X1 m. O2 Y
由于国内Google访问受限,所以上述命令不一定能下载成功。其实,我们现在可以从很多第三方渠道找到repo脚本,只需要取下来,确保repo可以正确执行即可。
4 m% J. c9 z$ ]2 w$ d
2. 工作原理
/ a8 A! h/ U* W
repo需要关注当前git库的数量、名称、路径等,有了这些基本信息,才能对这些git库进行操作。通过集中维护所有git库的清单,repo可以方便的从清单中获取git库的信息。 这份清单会随着版本演进升级而产生变化,同时也有一些本地的修改定制需求,所以,repo是通过一个git库来管理项目的清单文件的,这个git库名字叫manifests。

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

* I9 {9 _8 U* E: w
在客户端使用repo初始化一个项目时,就会从远程把manifests和repo这两个git库拷贝到本地,但这对于Android开发人员来说,又是近乎无形的(一般通过文件管理器,是无法看到这两个git库的)。 repo将自动化的管理信息都隐藏根目录的.repo子目录中。
) f; R  }" ~, y; l! I0 W/ ], s
2.1 项目清单库(.repo/manifests)
AOSP项目清单git库下,只有一个文件default.xml,是一个标准的XML,描述了当前repo管理的所有信息。
AOSP的default.xml的文件内容如下:
  1. <?xmlversion="1.0" encoding="UTF-8"?>4 a8 O  Q# C& j6 D9 Y6 [+ G) z
  2. <manifest>) H' [2 v/ G  E
  3. <remote name="aosp"
    - h7 l7 R4 l0 Z& w' J: b' E
  4. fetch=".."6 Z1 c5 A% r& z. Z  m6 W
  5. review="https://android-review.googlesource.com/" />5 M* z1 s$ h% C  ^
  6. <default revision="master"1 X8 Y' J9 l8 m) m
  7. remote="aosp"
    * y! k7 ?7 F- E; ~
  8. sync-j="4" />
    9 ?! o, L  K/ O, R$ F: e* R
  9. <project path="build"name="platform/build" groups="pdk,tradefed" >& N5 E% N' f3 |" X, E( j& _# e
  10. <copyfilesrc="core/root.mk" dest="Makefile" />6 r. s4 [! p7 e, y0 F5 H, K: \6 z
  11. </project>
    7 h3 Y2 n- G0 a5 \+ V
  12. <project path="abi/cpp"name="platform/abi/cpp" groups="pdk" />+ l, G0 y, d+ t' Z% o$ O
  13. <project path="art"name="platform/art" groups="pdk" />
    / o! L1 O5 ?: `5 |
  14. ...
    ) e3 {6 ^3 `5 C: z0 D( S
  15. <projectpath="tools/studio/translation"name="platform/tools/studio/translation"groups="notdefault,tools" />
    8 F' ^8 ^' w1 f4 I- \
  16. <project path="tools/swt"name="platform/tools/swt" groups="notdefault,tools" />8 [" e- ]0 Z8 m& M: R3 m% u2 B
  17. </manifest>
复制代码
3 f7 \& h' p# x+ m/ {$ b3 h) n$ v

, ]4 h, ?: c3 C$ Q& n1 o/ k
  • :描述了远程仓库的基本信息。name描述的是一个远程仓库的名称,通常我们看到的命名是origin;fetch用作项目名称的前缘,在构造项目仓库远程地址时使用到;review描述的是用作code review的server地址
  • :default标签的定义的属性,将作为标签的默认属性,在标签中,也可以重写这些属性。属性revision表示当前的版本,也就是我们俗称的分支;属性remote描述的是默认使用的远程仓库名称,即标签中name的属性值;属性sync-j表示在同步远程代码时,并发的任务数量,配置高的机器可以将这个值调大
  • :每一个repo管理的git库,就是对应到一个标签,path描述的是项目相对于远程仓库URL的路径,同时将作为对应的git库在本地代码的路径; name用于定义项目名称,命名方式采用的是整个项目URL的相对地址。 譬如,AOSP项目的URL为https://android. googlesource.com/,命名为platform/build的git库,访问的URL就是https ://android.googlesource.com/platform/build% C3 {4 @9 W$ ~% [3 V$ x

" v* N% e5 o3 \. \8 r2 F* p) v; _
如果需要新增或替换一些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的修改示例如下:
  1. $ ls.repo/local_manifests& ^* U, t2 H8 B7 e" r
  2. local_manifest.xml
    1 D4 F( j( s* L
  3. another_local_manifest.xml% d/ R4 F. W$ i2 A% ]" v: Z
  4. ' g+ o% s6 \6 W+ k1 c" _
  5. $ cat.repo/local_manifests/local_manifest.xml' J# d! Q7 _5 I
  6. <?xmlversion="1.0" encoding="UTF-8"?>' V1 E  e$ i) k( j" e% e) w
  7. <manifest>
    9 R" p% q. C; r! [0 Q/ j) W$ L
  8. <project path="manifest"name="tools/manifest" />
    ' j, K- t  ?) n/ C2 ^8 @
  9. <projectpath="platform-manifest" name="platform/manifest" />
    / Q" L6 P' m' h' S# {) y3 h
  10. </manifest>
复制代码

+ o- [. G/ r7 `( O, o
& b  |3 m3 b/ H9 G
2.2 repo脚本库(.repo/repo)
repo对git命令进行了封装,提供了一套repo的命令集(包括init,sync等),所有repo管理的自动化实现也都包含在这个git库中。 在第一次初始化的时候,repo会从远程把这个git库下载到本地。
* H3 ^% U) j' _$ {
2.3 仓库目录和工作目录
仓库目录保存的是历史信息和修改记录,工作目录保存的是当前版本的信息。一般来说,一个项目的Git仓库目录(默认为.git目录)是位于工作目录下面的,但是Git支持将一个项目的Git仓库目录和工作目录分开来存放。 对于repo管理而言,既有分开存放,也有位于工作目录存放的:
  • manifests: 仓库目录有两份拷贝,一份位于工作目录(.repo/manifests)的.git目录下,另一份独立存放于.repo/manifests.git
  • repo:仓库目录位于工作目录(.repo/repo)的.git目录下
  • project:所有被管理git库的仓库目录都是分开存放的,位于.repo/projects目录下。同时,也会保留工作目录的.git,但里面所有的文件都是到.repo的链接。这样,即做到了分开存放,也兼容了在工作目录下的所有git命令。
    3 j4 _9 H$ e) b) x9 V
既然.repo目录下保存了项目的所有信息,所有要拷贝一个项目时,只是需要拷贝这个目录就可以了。repo支持从本地已有的.repo中恢复原有的项目。

2 I0 H: c& M* [
2.4 repo 目录结构分析
  • .repo:/ g) J5 o( H0 v* M& L# }8 `  b
此为repo目录,可用于提取相应项目工作目录到外面的repo工作目录。
  • .repo/manifests.git:
    : V* w5 m) y3 K; k8 a' P
此为repo配置信息的git库,不同版本包含不同配置信息。每个repo项目初始化后也会有自己的git仓库的repo也会建立一个Git仓库,用来记录当前Android版本下各个子项目的Git仓库分别处于哪一个分支,这个仓库通常叫做:manifest仓库。
  • .repo/manifests:: p+ K! U4 b# p/ H4 c. B* k3 O
此为repo配置信息的工作目录(将配置信息的工作目录和相应的实际git目录分离管理,并且配置信息中的.git目录实际只是指向实际git库的软连接)。此目录中可能包含一个或多个xml文件描述的配置。每个xml文件是独立的一套配置,配置内容包括当前repo工作目录包含哪些git项目、所有git项目所处的默认公共分支、以及远端地址等。
  • .repo/manifest.xml:6 ]* b& ]0 u1 t% k
repo工作目录中的内容同一时刻只能采用manifests中的一个xml文件做为其配置,该文件就是其软连接,通过init的-m选项指定采用哪个文件;另外,同一xml文件也可能处于manifests库的不同版本或者不同分支,通过init的-b选项指定使用manifests中的哪个分支,每次init命令都会从服务器更新最新的配置。这里通过-m指定的manifests中的xml文件中。
  • .repo/repo:
    6 O" f( l: ]4 y5 ]
此为repo脚本集的git库,用于repo管理所需的各种脚本,repo的所有子命令就是其中的对应脚本实现。这些脚本也通过git管理,.repo/repo/.git为对其应的git目录,用git进行版本管理。

- G6 B2 R0 w) b0 _3. 使用介绍6 g/ d9 p0 f8 t& d
repo命令的使用格式如下所示:
  1. $ repo <COMMAND> <OPTIONS>
复制代码

! C* g. v2 T& V$ _9 s4 R7 r; @" a- B
( @3 [% J" m# Z
可选的的有:help、init、sync、upload、diff、download、forall、prune、start、status,每一个命令都有实际的使用场景, 下面我们先对这些命令做一个简要的介绍:

! q$ V, ?; T7 c3 D: ^$ O- _8 U9 d3.1 init
! X: {2 U; ~. E2 u! i
  1. $ repo init -u <URL> [<OPTIONS>]
复制代码
, P/ c+ J$ Y( j( C0 u: h
+ W, j# i& j# \7 K+ U% \
  • -u:指定manifests这个远程git库的URL,manifests库是整个项目的清单。默认情况,这个git库只包含了default.xml一个文件,其内容可以参见Android的样本
  • -m, –manifest-name:指定所需要的manifests库中的清单文件。默认情况下,会使用maniftests/default.xml
  • -b, –manifest-branch:指定manifest.xml文件中的一个版本,,也就是俗称的“分支”运行该命令后,会在当前目录下新建一个.repo子目录:% [6 z0 R3 ~7 M7 A
) v/ Y; a# Q) O9 Q# v
.repo
├── manifests # 一个git库,包含default.xml文件,用于描述repo所管理的git库的信息
├── manifests.git # manifest这个git库的实体,manifest/.git目录下的所有文件都会链接到该目录
├── manifest.xml # manifests/default.xml的一个软链接
├── repo # 一个git库,包含repo运行的所有脚本
这些本地的目录是如何生成的呢?执行repo命令时,可以通过–trace参数,来看实际发生了什么。
  1. $ repo --trace init -u $URL -b $BRANCH -m $MANIFEST% m4 i9 A' g  E* @& ?
  2.   --------------------
    7 S, G( @4 c4 _6 y
  3.   mkdir .repo; cd .repo
    ) E" ]6 c$ W. ~* T3 S8 @( [! n( r. D, K
  4.   git clone --bare $URL manifests.git; g  M# E7 u3 D9 o
  5.   git clone https://android.googlesource.com/tools/repo
    - S/ y6 k6 t( ~9 g' ?3 }
  6.   mkdir -p manifests/.git; cd manifests/.git
    4 b; A3 K- Y, r, a# g
  7.   for i in ../../manifests.git/*; do ln -s $ı .; done
    , H: i: d) N$ v0 k% V4 V
  8.   cd ..
    * P0 f! W+ ~1 c# ]& x9 L
  9.   git checkout $BRANCH -- .- W. g# f9 \7 U0 _
  10.   cd ..$ e1 ^; |( b, |; V. _
  11.   ln -s manifests/$MANIFEST manifest.xml
复制代码

$ q) W8 k7 K0 u- E' D5 a$ K& z2 Y5 {/ R7 j8 C) G
首先,在当前目录下创建.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命令了。

& _: y( H  R. `  S1 e0 K% S
我们还介绍几个不常用的参数,在国内下载Android源码时,会用到:
. X3 v' x+ N# x0 _5 R2 }5 f/ c
  • –repo-url:指定远程repo库的URL,默认情况是https: //android.googlesource.com/tools/repo,但国内访问Google受限,会导致这个库无法下载,从而导致repo init失败,所以可以通过该参数指定一个访问不受限的repo地址
  • –repo-branch:同manifest这个git库一样,repo这个git库也是有版本差异的,可以通过该参数来指定下载repo这个远程git库的特定分支
  • –no-repo-verify:在下载repo库时,会对repo的源码进行检查。通过–repo-url指定第三方repo库时,可能会导致检查不通过,所以可以配套使用该参数,强制不进行检查- ?, ~- b( K, i% U  K. l; \% U2 B
' M6 L. s) m6 Q8 }2 f
3.2 sync4 t( ?/ T" \1 y+ a
  1. $ repo sync [PROJECT_LIST]
复制代码
( m" r& U+ l1 L0 O  u. H6 N
3 H- Y4 ~2 C* ?' x/ b- y- D
下载远程代码,并将本地代码更新到最新,这个过程称为“同步”。如果不使用任何参数,那么会对所有repo管理的进行同步操作;也可以PROJECT_LIST参数,指定若干要同步的PROJECT。 根据本地git库代码不同,同步操作会有不同的行为:
2 ?. G3 e9 Z1 ^; ?0 m/ f1 q( i
  • 当本地的git库是第一次触发同步操作时,那么,该命令等价于git clone,会将远程git库直接拷贝到本地
  • 当本地已经触发过同步操作时,那么,该命令等价于git remote update && git rebase origin/,就是当前与本地分支所关联的远程分支 代码合并可能会产生冲突,当冲突出现时,只需要解决完冲突,然后执行git rebase --continue即可。
    6 s& ?2 o. Y. \
当sync命令正确执行完毕后,本地代码就同远程代码保持一致了。在一些场景下,我们会用到sync命令的一些参数:
! X, A* w2 w! o' _% W) [  k
  • -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的U R L / c l o n e . b u n d l e 来初始化本地的 g i t 库, c l o n e . b u n d l e 实际上是远程 g i t 库的镜像,通过 H T T P 直接下载,这会更好的利用网络带宽,加快下载速度。当服务器不能正常响应下载 URL/clone.bundle来初始化本地的git库,clone.bundle实际上是远程git库的镜像,通过HTTP直接下载,这会更好的利用网络带宽,加快下载速度。 当服务器不能正常响应下载URL/clone.bundle来初始化本地的git库,clone.bundle实际上是远程git库的镜像,通过HTTP直接下载,这会更好的利用网络带宽,加快下载速度。当服务器不能正常响应下载URL/clone.bundle,但git又能正常工作时,可以通过该参数,配置不下载$URL/clone.bundle,而是直接通过git下载远程git库
    ) L& W( Y3 h' R0 v
$ _- I$ ]( ]$ S  e0 g. L
3.3 upload
" z. Y, c1 a& V. `& t
  1. $ repo upload [PROJECT_LIST]
复制代码
4 G9 U# W! r& [, ]
3 n4 c$ k6 l8 c5 I+ X( |8 s  i3 v
从字面意思理解,upload就是要上传,将本地的代码上传到远程服务器。upload命令首先会找出本地分支从上一次同步操作以来发生的改动,然后会将这些改动生成Patch文件,上传至Gerrit服务器。 如果没有指定PROJECT_LIST,那么upload会找出所有git库的改动;如果某个git库有多个分支,upload会提供一个交互界面,提示选择其中若干个分支进行上传操作。

" W6 _0 I1 h( R6 `) W0 a* j
upload并不会直接将改动合并后远程的git库,而是需要先得到Reviewer批准。Reviewer查看改动内容、决定是否批准合入代码的操作,都是通过Gerrit完成。 Gerrit服务器的地址是在manifests中指定的:打开.repo/manifest.xml,这个XML TAG中的review属性值就是Review服务器的URL:
  1. <remote name="aosp"0 I2 F. g0 B7 f0 j( c
  2.         fetch=".."
    . N. U% |5 t" L% a  R
  3.         review="https://android-review.googlesource.com/" />
复制代码
! P, r1 I( e+ a  K& A$ J9 v& E7 Q
Gerrit的实现机制不是本文讨论的内容,但有几个与Gerrit相关的概念,是需要代码提交人员了解的:

3 H3 D& l, ^7 A0 ?/ o9 r
  • 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中,只有最新的一个是真正有用的,能够合并的。
    2 c% |- X9 |' o" ?% Z# |( _5 W2 V. v
9 ?, b# F7 Z* p- m6 |: [% B$ S$ B
3.4 download
- h1 [0 v" ]) T/ `5 z1 e
  1. $ repo download <TARGET> <CHANGE>
复制代码
  A/ ?" H. W1 v. g! i1 @+ ]9 h
( I/ I7 Y9 O+ `) I! h
upload是把改动内容提交到Gerrit,download是从Gerrit下载改动。与upload一样,download命令也是配合Gerrit使用的。

8 P2 t- ~. l+ h/ _& k$ V0 q
  • :指定要下载的PROJECT,譬如platform/frameworks/base, platform/packages/apps/Mms
  • :指定要下载的改动内容。这个值不是Commit-ID,也不是Change-ID,而是一个Review任务URL的最后几位数字。 譬如,AOSP的一个Review任务https: //android-review.googlesource.com/#/c/23823/,其中23823就是。
    / |% c" i& V4 y% Y3 g9 `  g
# N; [$ O1 o, B% v1 V6 M
3.5 forall
' P8 o. s2 l2 w5 X+ M* n, F
  1. $ repo forall [PROJECT_LIST] -c <COMMAND>
复制代码

! t+ ]7 c, T4 Y- S5 t2 B' a
1 }; Y8 P5 {: B) |. _
对指定的git库执行-c参数制定的命令序列。在管理多个git库时,这是一条非常实用的命令。PROJECT_LIST是以空格区分的,譬如:
$ repo forall frameworks/base packages/apps/Mms -c "git status"0 h& q+ N. r  }
表示对platform/frameworks/base和platform/packages/apps/Mms同时执行git status命令。 如果没有指定PROJECT_LIST,那么,会对repo管理的所有git库都同时执行命令。
, p- O. y, s* ~: X; E- x
该命令的还有一些其他参数:
$ [7 {- A( j( `6 L3 P% \
  • -r, –regex: 通过指定一个正则表达式,只有匹配的PROJECT,才会执行指定的命令
  • -p:输出结果中,打印PROJECT的名称
    0 _/ Y9 H/ w- G  q7 m
2 H6 k: G6 s. {. Q3 @: d2 r+ T
3.6 prune) }$ U% {/ j+ ]. ?( c2 w) k6 s
  1. $ repo prune [<PROJECT_LIST>]
复制代码

/ _5 S9 Y" ~9 a* O% Q- D0 b* L" `5 N3 F8 U% F" b$ }
删除指定PROJECT中,已经合并的分支。当在开发分支上代码已经合并到主干分支后,使用该命令就可以删除这个开发分支。
随着时间的演进,开发分支会越来越多,在多人开发同一个git库,多开发分支的情况会愈发明显,假设当前git库有如下分支:
  1. * master) f# M( _8 r6 p& d
  2.   dev_feature1_201501   # 已经合并到master% w6 F* j  b# k- W
  3.   dev_feature2_201502   # 已经合并到master/ K# r* L, \1 X; g! F, m
  4.   dev_feature3_201503   # 正在开发中,还有改动记录没有合并到master
复制代码

# `) v/ _8 F+ L
那么,针对该git库使用prune命令,会删除dev_feature1_201501和dev_feature2_201502。
定义删除无用的分支,能够提交团队的开发和管理效率。prune就是删除无用分支的”杀手锏“。

2 C5 Y- }+ ]6 @# s6 H7 p+ A# j* r3.7start& U+ @2 d, V5 Y6 \6 w' J* G5 g) x" t
  1. $ repo start <BRANCH_NAME> [<PROJECT_LIST>]
复制代码

3 E- C: {7 x; _- G' b
  X9 u/ h4 S/ n6 g. ^
在指定的PROJECT的上,切换到<BRANCH_NAME>指定的分支。可以使用–all参数对所有的PROJECT都执行分支切换操作。 该命令实际上是对git checkout命令的封装,<BRANCH_NAME>是自定义的,它将追踪manifest中指定的分支名。
; W7 n% }/ {- D! A2 c6 V4 O
当第一次sync完代码后,可以通过start命令将git库切换到开发分支,避免在匿名分支上工作导致丢失改动内容的情况。
1 V& ]* W, v; G3 L# j. Z( W& |4 N
3.8 status
2 R/ B5 ]  r0 y( o  b
  1. $ repo status [<PROJECT_LIST>]
复制代码
4 {- P- Y4 I( Z/ r6 U( ?; _

5 Z" ~; K+ z; V/ {0 U% v
status用于查看多个git库的状态。实际上,是对git status命令的封装。
4. 使用实践) k+ U, T$ E4 e' H
Android推荐的开发流程是:
  • repo init初始化工程,指定待下载的分支
  • repo sync下载代码
  • repo start将本地git库切换到开发分支(TOPIC BRANCH)
  • 在本地进行修改,验证后,提交到本地
  • repo upload上传到服务器,等待review
      D5 t; }, h: p! W- c) U

8 y! K% N8 y$ m4 b6 W) X4 y2 I
在实际使用过程中,我们会用到repo的一些什么子命令和参数呢?哪些参数有助于提高开发效率呢?下面我们以一些实际场景为例展开说明。

0 x5 m4 }! [' D( W9 {* a% _* @4.1 对项目清单文件进行定制
通过local_manifest机制,能够避免了直接修改default.xml,不会造成下次同步远程清单文件的冲突。
  Y' ?8 e: p; r  A
CyanogenMod(CM)适配了上百款机型,不同机型所涉及到的git库很可能是有差异的。以CM对清单文件的定制为例,通过新增local_manifest.xml,内容如下:
  1. <manifest>: y# ]: c$ ^) J8 Q1 P4 [) J* ?5 @! d
  2.     <!-- add github as a remote source -->
    ( U" ^* n' y% v% @- s
  3.     <remote name="github" fetch="git://github.com" />
    ; y1 R  N- {" \8 y0 n( F

  4. 4 c. ^  N: _( E& ]* L3 j+ |; f
  5. ( q; w, w1 b) q- I6 |- ]8 \' F+ D& @
  6.     <!-- remove aosp standard projects and replace with cyanogenmod versions -->7 _6 X% S, m6 q; Z# X7 K8 t* q
  7.     <remove-project name="platform/bootable/recovery" /># |4 {; K8 D/ m6 j! H$ r
  8.     <remove-project name="platform/external/yaffs2" />
    ! \4 g/ V: J6 ~9 @( I5 v- u
  9.     <remove-project name="platform/external/zlib" />
    : {' R  \' A. o$ e/ M
  10.     <project path="bootable/recovery" name="CyanogenMod/android_bootable_recovery" remote="github" revision="cm-10.1" />9 n* B  x' u9 i* i
  11. & S6 n, [9 z& C  R, P2 v" W- ~: X
  12.     <project path="external/yaffs2" name="CyanogenMod/android_external_yaffs2" remote="github" revision="cm-10.1" />
    1 ?- p+ B' A9 Y3 `0 }. z# w0 K
  13.     <project path="external/zlib" name="CyanogenMod/android_external_zlib" remote="github" revision="cm-10.1" />
    ! q! P8 N" c  T8 s0 s- _- d8 L: W
  14. # K; X6 y& N/ G% L* P
  15.     <!-- add busybox from the cyanogenmod repository -->
    / m5 }% i9 Y5 Z1 [  A8 r+ a
  16.     <project path="external/busybox" name="CyanogenMod/android_external_busybox" remote="github" revision="cm-10.1" />( ^% u+ o8 d& |' K# y8 C, z
  17.   [3 A5 `" h7 ]/ Q3 Y

  18. 8 y- O4 ?: A1 v# l9 z
  19. </manifest>
复制代码

) j, h9 }9 ~4 H: @( Z
local_manifest.xml会与已有的default.xml融合成一个项目清单文件manifest.xml,实现了对一些git库的替换和新增。 可以通过以下命令导出当前的清单文件,最终snapshot.xml就是融合后的版本:
  1. $ repo manifest -o snapshot.xml -r
复制代码
4 W# J2 S- w: d7 q, r# t
在编译之前,保存整个项目的清单,有助于问题的回溯。当项目的git库发生变更,需要回退到上一个版本进行验证的时候,只需要重新基于snapshot.xml初始化上一个版本即可:
  1. $ cp snapshot.xml .repo/manifests/
    & E/ @# f* T2 S
  2. $ repo init -m snapshot.xml         # -m 参数表示自定义manifest* L5 ]" r% ~, D
  3. $ repo sync -d                             # -d 参数表示从当前分支脱离,切换到manifest中定义的分支
复制代码

6 X, ?. u( d, V( N# C+ h& s- d
5 K' n5 l' J  I4.2 解决无法下载Android源码1 V9 q0 U  q' F
在repo init的时候,会从远程下载manifests和repo这两个git库,默认情况下,这两个git库的地址都是写死在repo这个python脚本里面的。对于AOSP而言,这两个git库的地址显然是google提供的。但由于google访问受限的缘故,会导致init时,无法下载manifests和repo。这时候,可以使用init的-u和–repo-url参数,自定义这两个库的地址,辅以–no-repo-verify来绕过代码检查。
  1. $ repo init --repo-url [PATH/TO/REPO] -u [PATH/TO/MANIFEST] -b. X  |& Z) ^6 U# x+ W8 ^; N" _0 ^% p
  2. [BRANCH] --no-repo-verify$ _) [$ v( k, v  J  B- b! g
  3. $ repo sync
复制代码
1 D3 |- u, E1 ]. R, \

  c& S" g* e8 j- D' H! g8 F8 G( V2 M% w2 U+ L
4.3 更快更省的下载远程代码
repo默认会同步git库的所有远程分支的代码,但实际开发过程中,用到的分支是有限的。使用sync的-c参数,可以只下载manifest中设定的分支,这会节省代码下载时间以及本地的磁盘空间:
  1. $ repo sync -c
复制代码
' M0 I2 j& U& r) H, Z; f
如果实际开发过程中,需要用到另外一个分支,而又不想被其他分支干扰,可以在已有的工程根目录下,使用如下命令:
  1. $ repo manifest -o snapshot.xml -r* g: {- w& ]. @9 ?. G& ~3 J4 ^: e0 L
  2. $ repo init -u [PATH/TO/MANIFEST] -b [ANOTHER_BRANCH]
    ( [, ?8 z  D( |7 r& D
  3. $ repo sync -c -d
复制代码

  W' S2 g" N' ?, t  E6 W5 X! a3 k4 A  ~+ H: \% W" M
以上命令序列,相当更新了manifest,而且仅仅只下载ANOTHER_BRANCH的代码,这样本地只保存了两个分支的代码。利用保存的snapshot.xml,还能将所有git库方便的切换回原来的分支。

# T6 b4 H$ w. o$ d
如果本地已经有一份Android源码,假设路径为~/android-exsit,想要下载另一份新的Android源码,通过–reference参数,在数分钟以内,就能将代码下载完毕:
  1. $ mkdir ~/android-new && cd ~/android-new8 E- ?) u; F0 }" t
  2. $ repo init --reference=~/android-exsit -u [PATH/TO/MANIFEST] -b [BRANCH]
    ! c$ L6 h" e% [( g7 C( g
  3. $ repo sync -c
复制代码

  I2 R3 T) U" i9 _ 4.4 避免在匿名分支上工作
在sync完代码后,所有git库默认都是在一个匿名分支上(no branch),很容易会由于误操作导致丢失代码修改。可以使用如下命令将所有的git库切换到开发分支:
  1. $ repo start BRANCH --all
复制代码
9 s8 b; I3 x$ u0 }

* H, D- {" @" j5 F- j+ m+ w; s4.5 使用upload提交代码
开发人员可能同时在多个git库,甚至多个分支上,同时进行修改,针对每个git库单独提交代码是繁琐的。可以使用如下命令,一并提交所有的修改:
  1. $ repo upload
复制代码

$ L5 @+ k3 U& a6 j- u8 M
不用担心会漏提交或者误提交,upload会提供一个交互界面,开发人员选择需要提交的git库和分支即可。
如果需要省去Gerrit上填写reviewer的操作,可以使用–reviewer参数指定Reviewer的邮箱地址:
  1. $ repo upload --reviewer="R.E.viewer@google.com"
复制代码

2 I" T  V% @+ l+ h& h/ ^  Y7 [% L/ }- b; n. g+ G  @- i
4.6 定期删除已经合并的开发分支
Git鼓励在修复Bug或者开发新的Feature时,都创建一个新的分支。创建Git分支的代价是很小的,而且速度很快,因此,不用担心创建Git分支的成本,而是尽可能多地使用分支。

7 a! U. B6 c+ B. o& u7 U" l" _
随着时间的演进,开发分支会越来越多,而一些已经合并到主干的开发分支是没有存在价值的,可以通过prune命令定期删除无用的开发分支:
  1. $ repo prune [PROJECT_LIST]
复制代码
% K7 Q$ J8 \1 e$ m- T' y

9 ~* y, P8 _: l' v. M; h. c+ b1 J4.7 同时操作多个git库
对于部分开发人员而言,同时操作多个git库是常态,如果针对每个git库的操作命令都是相同的,那么可以使用如下命令一次性完成所有操作:
  1. $ repo forall -c "git branch | grep tmp | xargs git branch -D; git branch"
复制代码

6 t4 n/ _6 f% P/ \& U
3 _; m0 N1 M' m7 w
参数-c指定的命令序列可以很复杂,多条命令只需要用“;”间隔。

. c2 A- b9 u3 L) e7 j4 Z
+ d; o3 ?4 q! Y, c" h

该用户从未签到

2#
发表于 2024-10-28 16:26 | 只看该作者
程序学习有教程吗

点评

目前还没有哈  详情 回复 发表于 2024-10-29 09:23

该用户从未签到

3#
 楼主| 发表于 2024-10-29 09:23 | 只看该作者

# ^3 w7 o# s4 c+ `0 Z目前还没有哈( D! X1 g) j5 \+ E& F7 d5 \( h% m
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-23 13:09 , Processed in 0.171875 second(s), 26 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表