2014年11月2日星期日

git、repo、gerrit之间的关系--学习感受第一篇

最近刚刚接触了repo、git、gerrit这些东东做开发,开始有点不爽,觉得命令行挺麻烦的,估计习惯了svn的结果吧。不过项目需要,还是硬着头皮学着。说说自己的浅见吧。1.git就是一个版本管理库,在git库中没有中心服务器的概念,大家都是平等的,真正的分布式。大家可以互相pull、push代码,非常方便。对于做定制很多的公司,其实挺好。2.repo,repo就是多个git库的管理工具。如果你只是改一个模块,git够用了,如果是多个git库同时管理,最好搞一个repo。当然你用gerrit,必须用repo了。3.gerrit,让审核代码,提交代码成了一件美好的事情。图形化的界面,简单的操作,让审核代码的人舒服了很多。

git及repo 命令

关于repo的一些命令 
1、怎样在代码文件夹下所有git库都建立分支? repo start --all <分支名>
2、怎样删除代码文件夹下所有git库的分支? repo abandon <分支名>

*1和2其实就是相反的动作

3、怎样知道代码文件夹下都改动了那些? repo diff
4、怎样把我的提交上传的gerrit上? repo upload

如果出现文件就把要提交的部分前面的#去掉

5、不想同步所有代码,只想同步一部分? repo sync <project1> <project2>……
6、repo upload以后后悔了,需要再修改一些?

(1)在原有分支上修改文件
(2)git commit --amend 
(3)repo upload --replace <工程名>
(4)出现文件以后去掉第1行的#,中括号里面填上 change-id


关于Git的一些命令 
1、repo init的时候我没有改用户名和email,现在发现不对了怎么办?

git config --global user.name "<your name>" ----修改用户名
git config --global user.email "<your email>" ----修改email
git config --list ----查看修改

2、想查看以前都有哪些提交? git log
3、想查看以前的两次提交有什么区别? git diff <log上显示的哈希值1> <log上显示的哈希值2>
4、想查看以前代码,但是现在修改的不能动 git checkout <哈希值>
5、想回到以前,现在修改的都不要了 git reset --hard <哈希值>
6、想把自己的修改提交到本地的git库里?

git add <文件名>
git commit -m "<注释>"

7、提交以后后悔了,文件还是要修改后的,只是不想要这次提交? git reset HEAD~1
8、提交以后发现少add了一个文件?

git add <忘记的文件>
git commit --amend

9、git add了很多文件,发现其中一个不想提交? git reset HEAD <file>
10、只想同步代码里的一个文件? git pull <file>
11、只删除中间的一个commit对应的修改,其他不变 git revert <对应的哈希值>


关于从A代码里面打包,放到B代码的问题

两种方法:

1)在A代码里面,$git diff <起始哈希值> <终点哈希值> > A.patch

在B代码里面,$git apply A.patch

有冲突解决冲突。

打开对应修改的文件发现就已经被修改了。

--这个方法的缺点是对应A代码的commit是不会加到B代码上的。

2)在A代码里面,$git format-patch -<x> <分支名> -o <路径>

-<x>,x填一个数字,表示从倒数第几个commit开始

运行完成就能在<路径>下看见<x>个patch,对应一个commit一个patch 在B代码里面,$git am <路径>/*.patch,就把所有的patch加到B代码里面

了。 --这个方法可以把A代码里面对应的commit都加到B代码上。

repo用法详解

Android 为企业提供一个新的市场,无论大企业,小企业都是处于同一个起跑线上。研究 Android 尤其是 Android 系统核心或者是驱动的开发,首先需要做的就是本地克隆建立一套 Android 版本库管理机制。

Android 使用 Git 作为代码管理工具,开发了 Gerrit 进行代码审核以便更好的对代码进行集中式管理,还开发了 Repo 命令行工具,对 Git 部分命令封装,将 百多个 Git 库有效的进行组织。要想克隆和管理这百多个 Git 库,还真不是一件简单的事情。

在研究 Repo 的过程中,发现很多文档在 Google Group 上,非"翻墙"不可看。非法的事情咱不干,直接阅读 repo 的代码吧。

创建本地 Android 版本库镜像的思路

如果了解了 Repo 的实现,参考 《Using Repo and Git》 , 建立一个本地的 android 版本库镜像还是不难的:

  • 下载 repo bootstrap 脚本
    $ curl http://android.git.kernel.org/repo >~/bin/repo
    $ chmod a+x ~/bin/repo
    $ export PATH=$PATH:~/bin
  • 提供 –mirror 参数调用 repo init ,建立 git 版本库克隆
    $ repo init -u git://android.git.kernel.org/platform/manifest.git --mirror
    • 使用 –morror 则下一步和源同步的时候,本地按照源的版本库组织方式进行组织,否则会按照 manifest.xml 指定的方式重新组织并检出到本地
  • 开始和源同步
    $ repo sync
  • 修改 manifest ,修改 git 库地址,指向本地的 git 服务器
    • 修改 platform/manifest.git 库中现有的 xml 文件,或者创建一个新的 xml 文件
    • 将 git 的地址改为本地地址,提交并 push
  • 本地 repo 镜像建立完毕之后,就可以在执行 repo init 时,使用本地更改后的 manifest 库,之后执行 repo sync 就是基于本地版本库进行同步了。
  • 也可以改造 repo,使得不必为 repo 工具初始化,也在本地网络完成操作…

Repo init 干了些什么?

实际上,得到客户使用 repo 的信息后,首先下载 repo 执行脚本开始研究。

curl http://android.git.kernel.org/repo >~/bin/repo

难道只有 600 行的 python 代码么?要是这样应该很简单的呀。可以看下来,却发现远非如此。

Shell script or python?

首先 repo 脚本使用了一个魔法:从脚本第一行的 shebang 来看应该是 shell 脚本,但是满眼却都是 python 语法,怎么回事?

 1 #!/bin/sh
2
3 ## repo default configuration
4 ##
5 REPO_URL='git://android.git.kernel.org/tools/repo.git'
6 REPO_REV='stable'
7
8 # Copyright (C) 2008 Google Inc.
...

22 magic='--calling-python-from-/bin/sh--'
23 """exec" python -E "$0" "$@" """#$magic"
24 if __name__ == '__main__':
25   import sys
26   if sys.argv[-1] == '#%s' % magic:
27     del sys.argv[-1]
28 del magic

魔法就在第 23 行,巧妙的通过 python 三引号字串写出了一个能被 python 和 shell script 都能理解的代码,以此为界,代码由 Shell 脚本进入了 Python 的世界。

Bootstrap 和真正的 repo

通过 curl 下载的的 repo 并非完整的 repo 脚本,只是一个 bootstrap。当 repo 执行时,会负责下载完整的 repo 代码,并将控制权转移给真正的 repo。

通过 main 函数,可以看到 repo 运行的开始,就试图发现本地真正的完整的 repo 代码,以便移交控制权:

544 def main(orig_args):
545   main, dir = _FindRepo()

586   try:
587     os.execv(main, me)

其中 545 行的 _FindRepo() 会在当前目录开始向上递归查找 ".repo/repo/main.py",如果找到则移交控制权(587行)。

Repo bootstrap 脚本调用 init 只完成第一阶段的初始化

Repo 的 bootstrap 脚本只支持两个命令 help 和 init,而 init 也只完成 repo 版本库克隆(即安装 repo 完整工具),之后就转移控制权。

在 Repo bootstrap 执行 init 可以提供很多参数,但实际上第一阶段初始化,只用到两个参数(而且都有默认值)

  • 参数:–repo-url=URL
    repo 工具本身的 git 库地址。缺省为:git://android.git.kernel.org/tools/repo.git
  • 参数:–repo-branch=REVISION
    使用 repo 的版本库,即 repo git 库的分支或者里程碑名称。缺省为 stable

第二阶段的 repo init

执行第二阶段的 repo init,控制权已经移交给刚刚克隆出来的 repo git 库的脚本。

Repo git 库被克隆/检出到执行 repo init 命令当前目录下的 .repo/repo 子目录中,主要的执行脚本为 .repo/repo/main.py。main.py 接着执行 repo init 命令。

Repo 的代码组织的非常好,在 .repo/repo/subcmds/ 子目录下,是各个 repo 命令的处理脚本。repo init 的第二阶段脚本正是由 .repo/repo/subcmds/init.py 负责执行的。第二阶段主要完成:

  • 克隆由 -u 参数提供的 manifest Git 库,如克隆 android 库时:
    $ repo init -u git://android.git.kernel.org/platform/manifest.git
  • 如果不提供 -b REVISION 或者 –manifest-branch=REVISION参数,则检出 manifest Git 库的 master 分支
  • 如果不提供 -m NAME.xml 或者 –manifest-name=NAME.xml 参数,则使用缺省值 default.xml
  • 如果提供 –mirror 参数,则后续同步操作会有相应的体现

Repo start 干了些什么?

Android 源码网站在介绍 repo 的使用模型中,有一个图片: http://source.android.com/images/git-repo-1.png , 介绍了 repo 的使用流程。其中 "repo start" 是紧接着 "repo sync" 后的第一个动作。那么这个动作是干什么的呢?

得益于 repo 对 git 操作的封装,"repo start" 命令的处理代码只有区区 68 行。

 37   def Execute(self, opt, args):

 41     nb = args[0]

 47     projects = []
48     if not opt.all:
49       projects = args[1:]

 54     all = self.GetProjects(projects)

57     for project in all:

59       if not project.StartBranch(nb):
60         err.append(project)

看到第 59 行了么,就是对 repo 同步下来的项目的多个 Git 版本库,逐一执行 project.StartBranch 操作。 nb 是 repo start 的第一个参数,即分支名称。

关于 StartBranch 的代码,在 project.py 中:

 857   def StartBranch(self, name):
858     """Create a new branch off the manifest's revision.
859     """

894     if GitCommand(self,
895                   ['checkout', '-b', branch.name, revid],
896                   capture_stdout = True,
897                   capture_stderr = True).Wait() == 0:
898       branch.Save()
899       return True

原来如此, repo start <branch_name> 就是逐一为各个版本库创建工作分支,以便在此分支下进行工作。

读者可以按图索骥,找到 repo 各个命令的实现,破解心中的疑惑。

 

repo的用法(zz)

注:repo只是google用Python脚本写的调用git的一个脚本,主要是用来下载、管理Android项目的软件仓库。(也就是说,他是用来管理给git管理的一个个仓库的)

下载 repo 的地址: http://android.git.kernel.org/repo ,可以用以下二者之一来下载 repo

wget http://android.git.kernel.org/repo 

或者 

curl http://android.git.kernel.org/repo > ~/bin/repo  

下载完成后须修改repo的权限: chmod a+x ~/bin/repo 

用repo sync 在抓去 android source code 的时候,会经常出现一些错误导致 repo sync 中断,每次都要手动开始。 可以用如下的命令,来自动重复

$?=1; 

while [ $? -ne 0 ] ; 

do  repo sync ; 

done

获取帮助:

repo help [ command ]   //显示command 的详细的帮助信息内容

示例: repo help init 来获取 repo init 的其他用法

repo init -u URL 用以在当前目录安装 repository ,会在当前目录创建一个目录 ".repo"  -u 参数指定一个URL, 从这个URL 中取得repository 的 manifest 文件。   

示例:repo init -u git://android.git.kernel.org/platform/manifest.git

获取的manifest文件放在.repo目录中。命名为manifest.xml。这个文件的内容其实就是所有被git管理的仓库的列表!

可以用 -m 参数来选择获取 repository 中的某一个特定的 manifest 文件,如果不具体指定,那么表示为默认的 namifest 文件 (default.xml)

repo init -u git://android.git.kernel.org/platform/manifest.git -m dalvik-plus.xml

(有诸多供我们选择的manifest文件,所有的manifest文件都放在目录.repo/manifests中,该目录本身亦被git所管理,你可以cd进去看看)

可以用 -b 参数来指定某个manifest 分支。

repo init -u git://android.git.kernel.org/platform/manifest.git -b release-1.0

你会发现.repo/manifests是个被git管理的仓库,这里放的是所有的manifest文件(*.xml),因为被git管理,固然有 分支,-b可以切换到你想要的分支然后再下载相关的xml文件,当然具体下载那个xml还要看-m参数了,所以如果你仅仅指定-b而没有-m的话,就是下 载-b指定分支下的default.xml文件

如果不指定-b参数,那么会默认使用master分支

4. repo sync [project-list]

下载最新本地工作文件,更新成功,这本地文件和repository 中的代码是一样的。 可以指定需要更新的project , 如果不指定任何参数,会同步整个所有的项目。

如果是第一次运行 repo sync , 则这个命令相当于 git clone ,会把 repository 中的所有内容都拷贝到本地。 如果不是第一次运行 repo sync , 则相当于 git remote update ;  git rebase origin/branch .  repo sync 会更新 .repo 下面的文件。 如果在merge 的过程中出现冲突, 这需要手动运行  git  rebase --continue

5. repo update[ project-list ]

上传修改的代码 ,如果你本地的代码有所修改,那么在运行 repo sync 的时候,会提示你上传修改的代码,所有修改的代码分支会上传到 Gerrit (基于web 的代码review 系统), Gerrit 受到上传的代码,会转换为一个个变更,从而可以让人们来review 修改的代码。

6. repo diff [ project-list ]

        显示提交的代码和当前工作目录代码之间的差异。

7. repo download  target revision

        下载特定的修改版本到本地, 例如:  repo download pltform/frameworks/base 1241 下载修改版本为 1241 的代码

8. repo start newbranchname .

        创建新的branch分支。 "." 代表当前工作的branch 分支。

9.  repo prune [project list]

        删除已经merge 的 project

10. repo forall -c 

这个命令会遍历所有的git仓库,并在每个仓库执行-c所指定的命令(这个被执行的命令就不限于仅仅是git命令了,而是任何被系统支持的命令,比如:ls 、 pwd 、cp 等等的 )

当我想通过这个命令遍历所有的仓库并在每个仓库执行"git checkout . "用以将每个仓库的改动都清除的时候,我这么输入命令:

repo forall -c git checkout . 

我发现这样根本不行。看来repo不能遍历执行checkout这个命令。今天我终于想到了另外一个命令"git reset --hard HEAD" 哈哈

repo forall -c git git reset --hard HEAD

再说一个新发现:以前用repo forall 执行一些命令的时候,可能再遍历到某个仓库的时候出了问题,但是我却苦于不知道这个仓库到底是哪个!一直也没有解决。今天终于找到了。。。。  关键时候还是要看命令自己带的帮助手册呀。。。

repo help forall  用这个命令查看下针对forall的帮助吧。说的很清楚,repo执行的时候加上-p参数就可以在遍历到每个仓库的时候先打印出当前的pwd,然后再继续执行-c所指定的命令。举例如下:

repo forall -p -c git branch    

 

//该命令会遍历所有仓库并打印每个仓库的分支情况,由于有了-p参数,这样便会打印出每个仓库的路径!!!

 

11. repo status

       显示 project 中每个仓库的状态,并打印仓库名称。

提供 –mirror 参数调用 repo init ,建立 git 版本库克隆

 repo init -u git://android.git.kernel.org/platform/manifest.git --mirror
  • 使用 –morror 则下一步和源同步的时候,本地按照源的版本库组织方式进行组织,否则会按照 manifest.xml 指定的方式重新组织并检出到本地

repo init 中指定manifest和branch的含义

比如:

repo init -u git://xxx/xxx/manifest.git -b <BRANCH> -m <MANIFEST>

 

这里-m和-b的含义是:

1. 注意到manifest.git本身也是一个git project

2. 所以,-b指定的是使用#1中这个git project的哪个branch

3. -m指定的是,下载该git project中的哪个文件(应该是首先切换了branch,然后再下载该文件)。

 

repo init这样做过了之后,其实本地就建立起来了一个.repo目录,里面核心文件就是manifest.xml,这个xml中就定义了:

- 包含哪些git project

- 每个git project使用什么branch

- 每个git project在本地磁盘上的路径和远端服务器上该git project的路径。 

 

事实上,当repo init完成,在本地的.repo/manifests目录下就可以看到manifest这个git project,可以用git branch -av来查看branch,用git log来查看日志。 


获取管理Android源代码中各个git仓库的xml配置文件

1、获取管理Android源代码中各个git仓库的 xml配置文件,这些配置文件是以git仓库 的方式进行管理的:

https://android.googlesource.com/platform/manifest 

git clone https://android.googlesource.com/platform/manifest

2、在clone的文件夹manifest中:

查看xml 配置文件git仓库中的branch信息:

$ git branch

* master

$ git branch -a

* master

  remotes/origin/HEAD -> origin/master

  remotes/origin/android-1.6_r1

  remotes/origin/android-1.6_r1.1

  remotes/origin/android-1.6_r1.2

  remotes/origin/android-1.6_r1.3

  remotes/origin/android-1.6_r1.4

  remotes/origin/android-1.6_r1.5

  remotes/origin/android-1.6_r2

  remotes/origin/android-2.0.1_r1

  remotes/origin/android-2.0_r1

  remotes/origin/android-2.1_r1

  remotes/origin/android-2.1_r2

  remotes/origin/android-2.1_r2.1p

  remotes/origin/android-2.1_r2.1p2

  remotes/origin/android-2.1_r2.1s

  remotes/origin/android-2.2.1_r1

  remotes/origin/android-2.2.1_r2

  remotes/origin/android-2.2.2_r1

  remotes/origin/android-2.2.3_r1

  remotes/origin/android-2.2.3_r2

  remotes/origin/android-2.2.3_r2.1

  remotes/origin/android-2.2_r1

  remotes/origin/android-2.2_r1.1

  remotes/origin/android-2.2_r1.2

  remotes/origin/android-2.2_r1.3

  remotes/origin/android-2.3.1_r1

  remotes/origin/android-2.3.2_r1

  remotes/origin/android-2.3.3_r1

  remotes/origin/android-2.3.3_r1.1

  remotes/origin/android-2.3.4_r0.9

  remotes/origin/android-2.3.4_r1

  remotes/origin/android-2.3.5_r1

  remotes/origin/android-2.3.6_r0.9

  remotes/origin/android-2.3.6_r1

  remotes/origin/android-2.3.7_r1

  remotes/origin/android-2.3_r1

  remotes/origin/android-4.0.1_r1

  remotes/origin/android-4.0.1_r1.1

  remotes/origin/android-4.0.1_r1.2

  remotes/origin/android-4.0.2_r1

  remotes/origin/android-4.0.3_r1

  remotes/origin/android-4.0.3_r1.1

  remotes/origin/android-4.0.4_r1

  remotes/origin/android-4.0.4_r1.1

  remotes/origin/android-4.0.4_r1.2

  remotes/origin/android-cts-2.2_r8

  remotes/origin/android-cts-2.3_r10

  remotes/origin/android-cts-2.3_r11

  remotes/origin/android-cts-2.3_r12

  remotes/origin/android-cts-4.0.3_r1

  remotes/origin/android-cts-4.0.3_r2

  remotes/origin/android-cts-4.0_r1

  remotes/origin/android-cts-verifier-4.0.3_r1

  remotes/origin/android-cts-verifier-4.0_r1

  remotes/origin/android-sdk-4.0.3-tools_r1

  remotes/origin/android-sdk-4.0.3_r1

  remotes/origin/android-sdk-adt_r16.0.1

  remotes/origin/froyo

  remotes/origin/gingerbread

  remotes/origin/gingerbread-release

  remotes/origin/ics-mr0

  remotes/origin/ics-mr1

  remotes/origin/master

  remotes/origin/tradefed

3、通过repo init下载Android源代码时通 过-b参数指定上面的branch信息即可,例如:

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

Windows下载android2.2完整源码

前两天看到有人给我留言,才发现很久没写过东西了。不过,最近也没做什么东西。下载了一个cygwin,在Windows下载android完整代码。
1、  下载安装cygwin。参考http://yuanzhifei89.iteye.com/blog/1117549
这个项目的名字来源于:GNU、Cygnus、Windows,3者的缩写。Cygwin的目标是:构建一套Windows下的Linux模拟环境。因此,Cygwin是一个庞大的项目,不只包括Linux下的开发环境,也包括工作环境,和各种各样的Linux下的软件。
http://cygwin.com/install.html该页面能够下载到一个网络安装程序setup.exe,然后通过它我们就可以更新和下载最新的工具版本了。
选择下载的地址,

获取android源码主要用到的工具:
cURL:cURL是一个利用URL语法在命令行方式下工作的文件传输工具。

git:android使用的版本控制软件。
windows版git(msysGit),带有gui的http://code.google.com/p/msysgit/downloads/list?can=3

repo:建立在git上的一个管理工具,简化git的使用的。它其实就是一个python脚本文件
这三个工具在cygwin安装的时候,需要选择一同安装。



选择三个工具后,下载依赖。

只需一路next,安装完成即可。
2、  使用cygwin
查看安装的工具是否安装好。
Git –version查看git版本,curl 查看curl;Python –V查看python版本。如下

接下来和在ubuntu上下载整个源码差不多。
//下载安装repo版本管理工具:
$curl http://android.git.kernel.org/repo> /home/android /bin/repo
$cd bin
$chmod a+x repo
失败的情况
注:图中的repo下载失败了。。。。。

$python ./bin/repo init -u git://android.git.kernel.org/platform/manifest.git –b froyo
失败,由于android.git.kernel.org不能访问。
1、使用git-repo项目,地址是http://code.google.com/p/git-repo/
我使用的版本为1.13 ,地址http://git-repo.googlecode.com/files/repo-1.13
为了保持使用的习惯,将repo-1.12改名为repo。
$ mv repo-1.12 repo
由于使用cygwin,我们可以在cygwin对应的目录下直接修改文件名。

3、  添加执行权限
$chmod a+x repo
4、  初始化repo
$python ./bin/repo init -u git://android.git.kernel.org/platform/manifest.git –b froyo
错误情况:
l  如果git失败,将git修改为http,下图即git连接失败
$python ./bin/repo init -u http://android.git.kernel.org/platform/manifest.git –b froyo



l  如果repo init时出现 https 443 ssl错误,那么需要将repo文件中https更改为http
$ sudo vi /user/bin/repo
将第五行
改为




由于android.git.kernel.org被黑,找个几个镜像地址。



1)获取 repo


2) 初始化


3)修改


修改文件中的

  <default revision="gingerbread"

           remote="github" />

将版本gingerbread替换为下载的相应版本。例如2.2则修改为froyo

Android 1.0

Android 1.5:Cupcake(杯子蛋糕)

Android 1.6:Donut(甜甜圈)

Android 2.0 / 2.1:éclair(闪电泡芙)

Android 2.2:Froyo(冷冻忧格)

Android 2.3:Gingerbread(姜饼)

Android 3.0:Honeycomb(蜂巢)

Android 4.0:Ice Cream Sandwich(冰淇淋三明治) 

3) Sync repo



下载repo


初始化

repo init -u git://codeaurora.org/platform/manifest.git -b froyo

但是如何要下载android源代码的话,需要把repo文件打开,把文件开头的


的这个地址换成其他可以下载源代码的地址久可以了,上面下载的repo就是把

替换成


这个地址下载速度太慢了,经常断掉。


5、  同步下载
Repo sync