Repo介紹[文章轉自CSDN- nwpushuai用戶]

  1. 目錄

請尊重原創版權,轉載註明出處。python

閱讀本文以前,須要對git有必定的瞭解。android

1. 概要

repo是Android爲了方便管理多個git庫而開發的Python腳本。repo的出現,並不是爲了取代git,而是爲了讓Android開發者更爲有效的利用git。git

Android源碼包含數百個git庫,僅僅是下載這麼多git庫就是一項繁重的任務,因此在下載源碼時,Android就引入了repo。 Android官方推薦下載repo的方法是經過Linux curl命令,下載完後,爲repo腳本添加可執行權限:github

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

因爲國內Google訪問受限,因此上述命令不必定能下載成功。其實,咱們如今能夠從不少第三方渠道找到repo腳本,只須要取下來,確保repo能夠正確執行便可。正則表達式

2. 工做原理

repo須要關注當前git庫的數量、名稱、路徑等,有了這些基本信息,才能對這些git庫進行操做。經過集中維護全部git庫的清單,repo能夠方便的從清單中獲取git庫的信息。 這份清單會隨着版本演進升級而產生變化,同時也有一些本地的修改定製需求,因此,repo是經過一個git庫來管理項目的清單文件的,這個git庫名字叫manifestsapi

當打開repo這個可執行的python腳本後,發現代碼量並不大(不超過1000行),難道僅這一個腳本就完成了AOSP數百個git庫的管理嗎?並不是如此。 repo是一系列腳本的集合,這些腳本也是經過git庫來維護的,這個git庫名字叫repo服務器

在客戶端使用repo初始化一個項目時,就會從遠程把manifestsrepo這兩個git庫拷貝到本地,但這對於Android開發人員來講,又是近乎無形的(通常經過文件管理器,是沒法看到這兩個git庫的)。 repo將自動化的管理信息都隱藏根目錄的.repo子目錄中。網絡

2.1 項目清單庫(.repo/manifests)

AOSP項目清單git庫下,只有一個文件default.xml,是一個標準的XML,描述了當前repo管理的全部信息。 AOSP的default.xml的文件內容以下:多線程

  1. <?xml version="1.0" encoding="UTF-8"?>併發

  2. <manifest>

  3. <remote name="aosp"

  4. fetch=".."

  5. review="https://android-review.googlesource.com/" />

  6. <default revision="master"

  7. remote="aosp"

  8. sync-j="4" />

  9. <project path="build" name="platform/build" groups="pdk,tradefed" >

  10. <copyfile src="core/root.mk" dest="Makefile" />

  11. </project>

  12. <project path="abi/cpp" name="platform/abi/cpp" groups="pdk" />

  13. <project path="art" name="platform/art" groups="pdk" />

  14. ...

  15. <project path="tools/studio/translation" name="platform/tools/studio/translation" groups="notdefault,tools" />

  16. <project path="tools/swt" name="platform/tools/swt" groups="notdefault,tools" />

  17. </manifest>

  • :描述了遠程倉庫的基本信息。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

若是須要新增或替換一些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>

2.2 repo腳本庫(.repo/repo)

repo對git命令進行了封裝,提供了一套repo的命令集(包括init, sync等),全部repo管理的自動化實現也都包含在這個git庫中。 在第一次初始化的時候,repo會從遠程把這個git庫下載到本地。

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命令。

既然.repo目錄下保存了項目的全部信息,全部要拷貝一個項目時,只是須要拷貝這個目錄就能夠了。repo支持從本地已有的.repo中恢復原有的項目。

3. 使用介紹

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子目錄:

  1. .repo

  2. ├── manifests # 一個git庫,包含default.xml文件,用於描述repo所管理的git庫的信息

  3. ├── manifests.git # manifest這個git庫的實體,manifest/.git目錄下的全部文件都會連接到該目錄

  4. ├── manifest.xml # manifests/default.xml的一個軟連接

  5. └── 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.com/tools/repo,但國內訪問Google受限,會致使這個庫沒法下載,從而致使repo init失敗,因此能夠經過該參數指定一個訪問不受限的repo地址

  • –repo-branch:同manifest這個git庫同樣,repo這個git庫也是有版本差別的,能夠經過該參數來指定下載repo這個遠程git庫的特定分支

  • –no-repo-verify:在下載repo庫時,會對repo的源碼進行檢查。經過–repo-url指定第三方repo庫時,可能會致使檢查不經過,因此能夠配套使用該參數,強制不進行檢查

3.2 sync

$ repo sync [PROJECT_LIST]

下載遠程代碼,並將本地代碼更新到最新,這個過程稱爲「同步」。若是不使用任何參數,那麼會對全部repo管理的進行同步操做;也能夠PROJECT_LIST參數,指定若干要同步的PROJECT。 根據本地git庫代碼不一樣,同步操做會有不一樣的行爲:

  • 當本地的git庫是第一次觸發同步操做時,那麼,該命令等價於git clone,會將遠程git庫直接拷貝到本地

  • 當本地已經觸發過同步操做時,那麼,該命令等價於git remote update && git rebase origin/<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庫

3.3 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,這個XML TAG中的review屬性值就是Review服務器的URL:

  1. <remote name="aosp"

  2. fetch=".."

  3. 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中,只有最新的一個是真正有用的,可以合併的。

3.4 download

$ repo download <TARGET> <CHANGE>

upload是把改動內容提交到Gerrit,download是從Gerrit下載改動。與upload同樣,download命令也是配合Gerrit使用的。

  • :指定要下載的PROJECT,譬如platform/frameworks/base, platform/packages/apps/Mms

  • :指定要下載的改動內容。這個值不是Commit-ID,也不是Change-ID,而是一個Review任務URL的最後幾位數字。 譬如,AOSP的一個Review任務https://android-review.googlesource.com/#/c/23823/,其中23823就是。

3.5 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的名稱

3.6 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就是刪除無用分支的」殺手鐗「。

3.7 start

$ repo start <BRANCH_NAME> [<PROJECT_LIST>]

在指定的PROJECT的上,切換到指定的分支。可使用–all參數對全部的PROJECT都執行分支切換操做。 該命令其實是對git checkout命令的封裝,是自定義的,它將追蹤manifest中指定的分支名。

當第一次sync完代碼後,能夠經過start命令將git庫切換到開發分支,避免在匿名分支上工做致使丟失改動內容的狀況。

3.8 status

$ repo status [<PROJECT_LIST>]

status用於查看多個git庫的狀態。實際上,是對git status命令的封裝。

4. 使用實踐

Android推薦的開發流程是:

  1. repo init初始化工程,指定待下載的分支
  2. repo sync下載代碼
  3. repo start將本地git庫切換到開發分支(TOPIC BRANCH)
  4. 在本地進行修改,驗證後,提交到本地
  5. repo upload上傳到服務器,等待review

在實際使用過程當中,咱們會用到repo的一些什麼子命令和參數呢?哪些參數有助於提升開發效率呢?下面咱們以一些實際場景爲例展開說明。

4.1 對項目清單文件進行定製

經過local_manifest機制,可以避免了直接修改default.xml,不會形成下次同步遠程清單文件的衝突。

CyanogenMod(CM)適配了上百款機型,不一樣機型所涉及到的git庫極可能是有差別的。以CM對清單文件的定製爲例,經過新增local_manifest.xml,內容以下:

  1. <manifest>

  2. <!-- add github as a remote source -->

  3. <remote name="github" fetch="git://github.com" />

  4. <!-- remove aosp standard projects and replace with cyanogenmod versions -->

  5. <remove-project name="platform/bootable/recovery" />

  6. <remove-project name="platform/external/yaffs2" />

  7. <remove-project name="platform/external/zlib" />

  8. <project path="bootable/recovery" name="CyanogenMod/android_bootable_recovery" remote="github" revision="cm-10.1" />

  9. <project path="external/yaffs2" name="CyanogenMod/android_external_yaffs2" remote="github" revision="cm-10.1" />

  10. <project path="external/zlib" name="CyanogenMod/android_external_zlib" remote="github" revision="cm-10.1" />

  11. <!-- add busybox from the cyanogenmod repository -->

  12. <project path="external/busybox" name="CyanogenMod/android_external_busybox" remote="github" revision="cm-10.1" />

  13. </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中定義的分支

4.2 解決沒法下載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

4.3 更快更省的下載遠程代碼

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

4.4 避免在匿名分支上工做

在sync完代碼後,全部git庫默認都是在一個匿名分支上(no branch),很容易會因爲誤操做致使丟失代碼修改。可使用以下命令將全部的git庫切換到開發分支:

$ repo start BRANCH --all

4.5 使用upload提交代碼

開發人員可能同時在多個git庫,甚至多個分支上,同時進行修改,針對每一個git庫單獨提交代碼是繁瑣的。可使用以下命令,一併提交全部的修改:

$ repo upload

不用擔憂會漏提交或者誤提交,upload會提供一個交互界面,開發人員選擇須要提交的git庫和分支便可。

若是須要省去Gerrit上填寫reviewer的操做,可使用–reviewer參數指定Reviewer的郵箱地址:

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

4.6 按期刪除已經合併的開發分支

Git鼓勵在修復Bug或者開發新的Feature時,都建立一個新的分支。建立Git分支的代價是很小的,並且速度很快,所以,不用擔憂建立Git分支的成本,而是儘量多地使用分支。

隨着時間的演進,開發分支會愈來愈多,而一些已經合併到主幹的開發分支是沒有存在價值的,能夠經過prune命令按期刪除無用的開發分支:

$ repo prune [PROJECT_LIST]

4.7 同時操做多個git庫

對於部分開發人員而言,同時操做多個git庫是常態,若是針對每一個git庫的操做命令都是相同的,那麼可使用以下命令一次性完成全部操做:

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

參數-c指定的命令序列能夠很複雜,多條命令只須要用「;」間隔。

相關文章
相關標籤/搜索