一. repo简介
repo是Google开发的用于管理Android版本库的一个工具,repo是使用Python对git进行了一定的封装,并不是用于取代git,它简化了对多个Git版本库的管理。用repo管理的版本库都需要使用git命令来进行操作。

1 介绍清单库文件

<manifest>
  <remote fetch="ssh://10.XX.XX.XX" name="origin" review="10.XX.XX.XX:8090"/>
  <default remote="origin" revision="develop" sync-j="4"/>

  <project groups='all' name="XXX/XXX" path="XXX/XXX" remote="origin" revision="develop">
    <linkfile dest="XX/XX/" src="../.."/>
    <copyfile dest="XX/XX/XX.txt" src="./xx.txt"/>
  </project>
  <project name="XXX/repohooks" path="XXX/XXX" remote="origin" revision="develop"/>
  <repo-hooks enabled-list="post-sync" in-project="XXX/repohooks"/>
</manifest>

清单文件是以xml的格式组织的,一个清单库可以包含多个清单文件和多个分支,每个清单文件和分支都有对应的版本。

常用参数:

1.1 remote 描述了远程仓库的基本信息
name:远程仓库的名称,通常我们看到的命名是origin
fetch:git库地址
review:用作code review的server地址

1.2 project: 每一个repo管理的git库,就是对应到一个标签
path:把代码下载到指定目录下
name:该项目远程版本库的相对路径
groups:该项目远程版本库所属组
revision:单独指定分支
remote:单独指定远程版本库名称

1.3 default元素: default标签定义的属性,将作为标签的默认属性,在标签中,也可以重写这些属性
revision:默认分支
remote:默认的远程版本库名称
sync -j:表示在同步远程代码时,并发的任务数量

1.4 :project元素下的子元素copyfile,定义clone后从src到dest拷贝操作。

1.5 :project元素下的子元素linkfile,定义clone后dest到src的软连接。

1.6 :repo钩子,in-project下面应该有一个与钩子同名的python文件。因此,如果您想支持预上传钩子,您需要创建一个名为post-sync.py的文件。Repo将在处理钩子时动态加载该模块,然后调用其中的主函数。
in-project:定义挂钩的项目。该值必须与先前定义的元素的name属性(而不是属性)匹配。
enabled-list:要使用的钩子列表,空格或逗号分隔。

1.7 remove-project
从内部的manifest表中删除指定的project。经常用于本地的manifest文件,用户可以替换一个project的定义。

2 下载repo代码
新建项目存放的文件夹
mkdir mstar648

进入到项目文件夹
cd mstar648

执行代码拉取的命令
repo init -u ssh://ppgerrit.com/Mstar648/manifest.git -b 648_cultraview -m ppos4.5.0_cultraview.xml
此时会在当前目录下生成一个.repo的文件。

二.repo常用命令
1 解析拉取代码的命令(repo init)

Usage:
	repo init -u URL [OPTIONS]

Options:
-u:指定manifest项目清单库地址。
-m,–manifest-name:指定manifests库中的清单文件,默认为maniftests/default.xml。
-b, –manifest-branch:指定manifest仓的分支,默认为master分支。
-g:指定manifests库中的组来下载代码,默认为all。

不常用参数:
–-repo-url:指定远程repo库地址,当引导脚本中的地址不可访问时,可以通过该参数指定可访问的repo地址。
–-repo-branch:同manifest这个git库一样,repo这个git库也是有版本差异的,可以通过该参数来指定下载repo这个远程git库的特定分支。
–-no-repo-verify:在下载repo库时,会对repo的源码进行检查。通过–repo-url指定第三方repo库时,可能会导致检查不通过,所以可以配套使用该参数,强制不进行检查。
--depth {number}:限制下载记录次数,加速代码下载。

例如我们拉取代码的时候的命令如下:
repo init -u ssh://ppgerrit.com/Mstar648/manifest.git -b 648_cultraview -m ppos4.5.0_cultraview.xml

repo init要完成如下操作:
完成repo工具的完整下载,执行的repo脚本只是引导程序
clone清单库manifest.git (地址是-u后面的参数)
clone的清单库位于manifest.git中,clone到本地.repo/manifest中,.repo/manifest.xml只是符号链接,它指向的是.repo/manifests/default.xml

如果manifest中有多个xml文件,repo init可以任意选择其中一个,默认选择的是default.xml。
上面的拉取代码示例选择的是ppos4.5.0_cultraview.xml里面的配置,那么.repo/manifest.xml指向.repo/manifests/ppos4.5.0_cultraview.xml

2 同步代码(repo sync)

Usage:
	repo sync [<project>...]

常用参数:
-j:开启多线程同步操作,这会加快sync命令的执行速度。该参数在default.xml中有默认设置。
-c, –current-branch:只同步指定的远程分支。默认情况下,sync会同步所有的远程分支。

不常用参数:
-d, –detach:脱离当前的本地分支,切换到manifest.xml中设定的分支。
-f, –force-broken:当有git库sync失败了,不中断整个同步操作,继续同步其他的git库。
–no-clone-bundle:在向服务器发起请求时,为了做到尽快的响应速度,会用到内容分发网络(CDN, Content Delivery Network)。

执行了repo init 命令后,我们需要执行如下命令同步代码:
repo sync -c -q -j8

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

1> 当本地的git库是第一次触发同步操作时。该命令等价于git clone,会将远程git库直接拷贝到本地。
2> 当本地已经触发过同步操作时。该命令等价于git remote update && git rebase origin/,就是当前与本地分支所关联的远程分支。在代码合并时可能会产生冲突,当冲突出现时,只需要解决完冲突,然后执行git rebase --continue即可。

git remote update
相当于对每一个remote源执行了fetch操作
git rebase origin/branch
对当前分支的跟踪分支执行rebase操作

3.repo upload 上传代码

repo upload [PROJECT_LIST]
命令说明:

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

执行repo upload之前,需保证代码已经commit。

不常用参数:
-re
当有多个git库的改动提交时,为了避免在网页上频繁的填选Reviewer这种重复劳动, upload提供了–re, –reviewer参数,在命令行一次性指定Reviewer。

4 创建并切换分支(repo start)

Usage:
repo start <BRANCH_NAME> [<PROJECT_LIST>]
repo start 的实质就是对git checkout -b 的封装

常用参数:
--all:对所有的PROJECT都执行分支切换操作

刚clone下来的代码是没有分支的,例如:
repo start 的实质就是对git checkout -b 的封装,可以为单个项目或所有项目以清单文件中已设定的分支为基础,在本地创建新的分支。

repo start 与 git checkout -b 的区别:
repo start 是在清单文件设定的分支基础上创建新的分支
git checkout -b 是在当前所在分支的基础上创建新的分支
如果清单文件中设定的分支是remoteBranchName,创建新的分支localBranchName。

**单个项目创建localBranchName分支的命令如下:**
repo start localBranchName 项目绝对路径

**为所有项目创建localBranchName分支的命令如下:**
repo start localBranchName --all

5 查看分支(repo branches)

Usage:
	repo branches [<project>...]

本文下面所有的命令都是在单个项目中运行!!!
例如查看上面拉取的mstar648项目下各模块的分支信息

在mstar648项目下运行如下命令:
repo branches

6 切换分支(repo checkout)

Usage:
	repo checkout <branch name> [<project>...]

该命令实际是对git checkout命令的封装。检出之前由repo start创建的分支。

切换分支语法:
repo checkout brancnName

7.查看工作区文件差异(repo diff)

Usage:
	repo diff [<project>...]
Example:
	repo diff           ---查看所有项目
	repo diff platform/build  platform/bionic  ---只查看其中两个项目

实际是对git diff命令的封装,用于分别显示各个项目工作区下的文件差异。

8 repo stage(把文件添加到index表中)
实际是对git add –interactive命令的封装、用于挑选各个项目工作区中的改动以加入暂存区。

Usage:
	repo stage -i [<project>…]
-i代表git add –interactive命令中的–interactive,给出个界面供用户选择。

9 repo prune(删除已经合并分支)
实际上是对git branch –d命令的封装,该命令用于扫面项目的各个分支,并删除已经合并的分支,用法如下:

repo prune [<project>…]

命令说明:
删除指定PROJECT中,已经合并的分支。当在开发分支上代码已经合并到主干分支后,使用该命令就可以删除这个开发分支。随着时间的演进,开发分支会越来越多,在多人开发同一个git库,多开发分支的情况会愈发明显,假设当前git库有如下分支:

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

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

10. 查看文件状态(repo status)
该命令实际上是对git diff-index 和git diff-filse命令的封装,同时显示暂存区的状态和本地文件修改的状态
查看文件状态语法:

repo status [<PROJECT_LIST>]

其中:
每个小节的第一行显示的是项目名称和所在的分支名称
每个小节的第二行的第一个字母表示暂存区文件修改的状态
-:没有改变
A:添加(不在HEAD中,在暂存区中)
M:修改(在HEAD中,在暂存区中,内容不同)
D:删除(在HEAD中,不在暂存区)
R:重命名(不在HEAD中,在暂存区,路径修改)
C:拷贝(不在HEAD中,在暂存区,从其他文件拷贝)
T:文件状态改变(在HEAD中,在暂存区,内容相同)
U:未合并,需要冲突解决

每个小节的第二行的第二个字母表示工作区文件的更改状态
-:新/未知(不在暂存区,在工作区)
m:修改(在暂存区,在工作区,被修改)
d:删除(在暂存区,不在工作区

11. 删除指定分支(repo abandon)
该命令实质是对git branch -D的封装,语法如下:
repo abandon branchName

12 设置远程仓库(repo remote)
语法如下:
repo remote addd remoteName url
例如:
repo remote add org ssh://172.16.1.31/git_repo
该命令是根据xml文件添加的远程分支,方便于向服务器提交代码,执行后的build目录下看到新的远程分支是org。

13 遍历当前项目下的所有git仓库(repo forall)
该命令相当于一个迭代器,会遍历当前项目下所有的git仓库,在所有指定的项目中执行同一个shell命令。

语法如下:
    repo forall [PROJECT_LIST] -c <COMMAND>
    
其中,参数有:
-c:后面可以带的任何可以被系统支持的shell命令(ls,cp,pwd等)
-p:在shell命令输出之前列出项目名称
-v:列出执行shell指令输出的错误信息

该命令还可以添加环境变量:
环境变量的参数有如下几种:
REPO_PROJECT:指定项目的名称
REPO_PATH:指定项目在工作区的相对路径
REPO_REMOTE:指定项目远程仓库的名称
REPO_LREV:指定项目最后一次提交服务器仓库对应的哈希值
REPO_RREV:指定项目在克隆时的指定分支,manifest里的revision属性

如果-c后面的shell指令是上述的环境变量,则需要用单引号把shell指令括起来。

语法如下:
repo forall –c ‘echo $REPO_PROJECT’

14.添加环境变量

repo forall –c ‘echo $REPO_PROJECT’
repo forall  –c ‘echo $REPO_PATH’

15 合并多个分支(merge)
例如将所有项目都切换到master分支。

repo forall -p -c git merge local

上面的命令就是将local分支合并到master分支上

16 打标签(tag)
在所有项目下打标签的命令如下:

repo forall -c git tag 标签名

17 显示版本号(repo version)
repo安装后,可以通过repo version命令查看版本号。

15.branch(创建特性分支)

repo forall –c git branch crane-dev
repo forall –c git checkout –b crane-dev

18.repo download 下载代码

repo download <TARGET> <CHANGE>
命令说明:

download是从Gerrit下载改动代码。
<TARGET>:指定要下载的PROJECT,譬如Tapp/eCommApp
<CHANGE>:指定要下载的改动内容的change_number。

19.repo manifest 查看manifest

repo manifest -r -o %s_manifest.xml
常用参数:

-r:保存当前分支revisions。
-o NAME.xml:输出xml文件名。

命令说明:
用于显示manifest文件内容。

三.实际需求:
1.repo重新与服务器同步(仅适用于只修改了文件,没有增加文件和变换文件存放位置)
repo forall -c git reset --hard HEAD
repo sync -c -q -j8

2.当我们repo init通过-b指定一个分支时,比如主分支maindev,但是具体仓库下面可能是其他分支情况。
可以通过.repo/manifests下查找对应的分支情况。
grep -nr “当前本地分支” ./
具体结果解析为:
name: gerrit服务器上远程分支名
path: 本地仓库实际路径
upstream:当前对应分支名

3.repo忽略本地修改,强制恢复初始

# 将HEAD强制指向manifest的库,而忽略本地的改动。
repo sync -d

# Remove all working directory (and staged) changes.
repo forall -c 'git reset --hard'   

# Clean untracked files
repo forall -c 'git clean -f -d'   

# 拉代码
repo sync -c -q -j16

此时拉下来的代码,没有指向具体分支。
HEAD detached at d928195607
nothing to commit, working tree clean

需要使用如下命令
创建本地分支并切换到该分支上去。
repo start master-branch --all
这个master-branch名字即是远程分支名也是本地分支名字。

4.查看当前仓库对应的分支

1.repo init 下载repo
2.cd .repo/manifests/submanifests/ 查看对应的xml,
如android_aosp.xml android_platform.xml 
3.grep -nr "vendor/external/ffmpeg" *
4.可以得出该仓库对应的远程分支名