repo支持"push"的两种改法

2年前写过一篇关于git-repo工作流的博客,直到最近项目组引入了新的芯片平台才有机会运用,采用了与上游代码库一致的repo组织方式。但是repo一般需要与gerrit服务器进行配合,使用upload命令进行代码上传与评审,无法直接适用组内的gitlab。而repo本身是python语言开发的,初步了解原因后着手定制化也就势在必行了。
当然,还好国内的git指南一书作者”蒋鑫”前辈早早的已经解决了这个问题,实现了repo push的子命令并开源在github上 t/repo-push-subcommand · ossxp-com/repo@1ce94da,可以直接支持多个仓库推送本地分支到远程的类git服务器。有兴趣的同学可以借鉴里面的实现,重点关注这次提交:

1
2
3
4
commit 1ce94da1e358a8b0c24eb25dda8a47cb7ef58bc7
t/repo-push-subcommand
New repo push subcommand, which push to Git server directly,
not through the Gerrit Server.

只是,多少认为copy一份upload的代码再修改为push的做法不够优雅,本着一切重复都是可以优化的原则,自然的想到尝试在upload命令直接兼容push操作的方式,最后只需要使用upload时需要添加–pm参数即可,也一并开源在本人github的group里 rerepo/reopen_repo at tm-devdev
另外,里面还有一些其它的特性改进,如下:

  • repo init -b –local-only【更新manifest但不fetch】
  • repo sync –no-repo-sync【跳过repo库更新】
  • repo sync –track【使得快照版的xml可以创建分支】
  • repo diffmanifest –branch【避免手动生成xml】

PS:再安利一篇”老罗”的博文 repo源码分析,内容非常详尽,读过后会对repo实现逻辑会有通透的了解,二次开发repo就已然成竹在胸:)


版权声明

作者: Marcus Tang
许可证: 创作共用保留署名-非商业-禁止演绎4.0国际许可证
License: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
本文永久链接: https://blog.tmc1900.com/rerepo-repo/

opensource 的循环依赖

这篇文章主要记录下在openssl和一些依赖ssl的应用程序的编译过程中,遇到的一些问题和和思考。

一个原则

通常源代码编译应该是只包含3个步骤

  1. 配置
  2. 编译
  3. 部署

配置多种多样,但编译理应与配置区配开来,不需要再进行相似的配置,理想情况只需一个 make 即可

循环依赖

最近升级opensource模块,遇到一个很有趣的情况,需要编译的模块如下:

  • openssl
  • libssh2
  • curl

正常情况的依赖关系是这样的:

1
2
curl +-> libssh2 +-> openssl
+--------------------^

然而引用的 openssl-1.0.1h-cmp 开源模块在默认配置下造成了如下的依赖关系:

1
2
curl +-> libssh2 +-> openssl
^--------------------^

这样在编译 curl 时,会产生一个很微妙的问题,先编译的 openssl 由于在一开始只是编译库而没有链接可执行文件,所以没有问题,但是到了链接 curl 时,会报出在libssl里找不到 libcurl 的接口为定义的问题,也就是说 curl 自己来依赖一个自己还没有编译出来的自己的接口的循环依赖问题!

所以依赖管理对于多模块的项目是是很重要的,对于 openssl-1.0.1h-cmp 模块依赖 curl 的选项本来就不应该提供,所以默认不要去打开就可以了。

源代码获取

openssl

https://www.openssl.org/source/

下载 openssl-1.1.0c.tar.gz

另外,有依赖问题的cmp版本openssl源自 cmpforopenssl 项目

因为是svn项目,用svn检出

1
2
3
4
5
$ svn checkout http://svn.code.sf.net/p/cmpforopenssl/code/src/openssl-1.0.1h-cmp

$ svn info
Last Changed Rev: 782
Last Changed Date: 2016-10-28 17:04:07 +0800 (Fri, 28 Oct 2016)

编译前先保证svn仓库的清爽

1
2
$ svn revert -R .
$ svn status | grep "^? " | awk '{print $2}' | xargs rm -rf

libssh2

https://www.libssh2.org/download/

下载 libssh2-1.7.0.tar.gz

curl

https://curl.haxx.se/download/

下载 curl-7.50.3.tar.gz

源代码编译

toolchain

交叉编译工具版本

1
2
$ arm-none-linux-gnueabi-gcc -v
gcc version 4.8.1 (Sourcery CodeBench Lite 2013.11-33)

编译openssl

openssl 采用了自有的编译脚本,而非GNU标准的 configure 配置脚本
另外,这里指定了一下 linux-generic32 的目标,为了在64位系统上编译32位的可执行程序

1
2
3
4
5
$ export CROSS_COMPILE=arm-none-linux-gnueabi-
$ ./Configure linux-generic32 no-asm shared --prefix=$HOME/opt/openssl
$ make update
$ make
$ make install

如果没有一开始export,那么就要这样编译

1
2
$ make MAKEDEPPROG=arm-none-linux-gnueabi-gcc update
$ make CROSS_COMPILE=arm-none-linux-gnueabi-

这里其实有个问题,执行完 config 可以看到 CC=gcc,在 make 时通过 CROSS_COMPILE=arm-none-linux-gnueabi- 这个“常用”变量来添加交叉编译前缀,这样多少导致“配置”的内容让“编译”步骤又配置了一次
其实,在官方的 openssl-1.1.x 版本开始已经支持 –cross-compile-prefix 参数来指定交叉编译器版本

1
2
$ ./Configure linux-generic32 no-asm shared --prefix=$HOME/opt/openssl --cross-compile-prefix=arm-none-linux-gnueabi-
$ make

libssh

来到libssh,总算回到了标准GNU工具automake自动生成的 configure 脚本,交叉编译都是通过经典的 –host 等参数指定

这里本想使libssh安装到和openssl不同的路径进行配置

1
$ ./configure --prefix=$HOME/opt/libssh --host=arm-none-linux-gnueabi --with-openssl --with-libssl-prefix=$HOME/opt/openssl --disable-examples-build

但在编译时出现了问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ make
CCLD libssh2.la
make[2]: Leaving directory `/home/ubuntu/git/libssh2-1.7.0/src'
make[1]: Leaving directory `/home/ubuntu/git/libssh2-1.7.0/src'
Making all in tests
make[1]: Entering directory `/home/ubuntu/git/libssh2-1.7.0/tests'
CC ssh2.o
CCLD ssh2
/home/ubuntu/opt/arm-2013.11/bin/../lib/gcc/arm-none-linux-gnueabi/4.8.1/../../../../arm-none-linux-gnueabi/bin/ld: warning: libssl.so.1.0.0, needed by ../src/.libs/libssh2.so, not found (try using -rpath or -rpath-link)
/home/ubuntu/opt/arm-2013.11/bin/../lib/gcc/arm-none-linux-gnueabi/4.8.1/../../../../arm-none-linux-gnueabi/bin/ld: warning: libcrypto.so.1.0.0, needed by ../src/.libs/libssh2.so, not found (try using -rpath or -rpath-link)
../src/.libs/libssh2.so: undefined reference to `BN_rand'
../src/.libs/libssh2.so: undefined reference to `EVP_CipherInit'
../src/.libs/libssh2.so: undefined reference to `BN_set_word'
../src/.libs/libssh2.so: undefined reference to `EVP_PKEY_get1_DSA'

这里根据提示 try using -rpath or -rpath-link 来补充些 LDFLAGS 变量

1
$ make LDFLAGS=-R$HOME/opt/openssl/lib

注意:虽然这样可以编译通过,但是会带来另外的问题(下面再展开讨论)

再次为了避免 make 使用额外的参数,调整一下 –prefix 参数使用和 openssl 一样的路径配置

1
2
3
$ ./configure --prefix=$HOME/opt/openssl --host=arm-none-linux-gnueabi --with-openssl --with-libssl-prefix=$HOME/opt/openssl --disable-examples-build
$ make
$ make install

curl

这里继续配置相同的安装路径,避免最后链接 curl 时找不到库路径的问题
编译部署完成后,在 $HOME/opt/openssl 的bin目录下终于能看了一个可执行的 curl 程序 :)

1
2
3
$ ./configure --prefix=$HOME/opt/openssl --host=arm-none-linux-gnueabi --with-ssl=$HOME/opt/openssl --with-libssh2=$HOME/opt/openssl
$ make
$ make install

最后,通过 readelf 来观察一下 curl 的库依赖情况

1
2
3
4
5
6
7
8
9
$ arm-none-linux-gnueabi-readelf -d bin/curl
Dynamic section at offset 0x44698 contains 29 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libcurl.so.4]
0x00000001 (NEEDED) Shared library: [libssl.so.1.0.0]
0x00000001 (NEEDED) Shared library: [libcrypto.so.1.0.0]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000f (RPATH) Library rpath: [/home/ubuntu/opt/openssl/lib]

重点注意 RPATH Library rpath: [/home/ubuntu/opt/openssl/lib] 的内容,这里如果是编译本机运行的程序是没有问题的,但是在交叉编译的情境下多多少少就会有不合理的地方
因为 /home/ubuntu/opt/openssl/lib 的路径是编译机的路径,而部署到目标机上可能根本就不存在这样的目录
所以,回过来看 make install 其实还不是真正的“部署”,configure 脚本还差一个指定目标机运行时库路径的参数,automake工具(更准确说是gcc工具链)直接的把编译时指定的路径用作运行时路径是不够的。

后记

其实,编译 opensource 模块本身并不困难,真正的挑战在于将这些模块整合进自己的项目中,并且要一起构建整个项目群时,才会遇到超出 automake 工具能解决的问题。由于 configure 一般要求依赖模块已经存在,这就导致需要在编译一个模块前先 make 依赖的模块,最后总的 makefile 很可能就变成了一个 makefile.sh 的形式。这时考验这个 makescript 的一个关键就是是否会在每次 make 时都需要重新 configure 了。
当然,将模块在外部 configure 后再加入项目版本控制肯定是不推荐的,比较好的方法是采用 prebuilt 预编译管理开源模块。
另外,关于 make install 又有个问题就是 make 一个模块 install 一个,还是最后统一遍历 install 呢?对此就保留下建议了。
总之,没有最好的方法,切合自身项目的构建需要才是最重要的,而且如果编译能做到尽可能优化最终节省的还是整个 team 的时间。
这次先总结到这里,欢迎留言讨论指正。


版权声明

作者: Marcus Tang
许可证: 创作共用保留署名-非商业-禁止演绎4.0国际许可证
License: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
本文永久链接: https://blog.tmc1900.com/open-build/

北京之行

2016年十月

趁着国庆节放假的时间,也背上包坐上车到了读书时就一直想去的一座城,北京。
究其原因可能是因为一句话吧,“围城:城中的人想出去,城外的人想进来”。

一般都说过程比结果更重要,但这次反过来想直接说下结果,很遗憾最后一天没有到得了长城。
所以,迟早是要再去一次的。。
这里,只能附上一张北京地铁的照片了,下次一定要多坐坐地上的公交,多走走路:)

另外,还是有幸运的地方的,来北京的主题之一看中网圆满了。
由于提前查不到赛程,不知道BIG4穆雷是几号比赛,碰碰运气的选在了5号看,结果很巧,感受一下现场的气氛吧~


版权声明

作者: Marcus Tang
许可证: 创作共用保留署名-非商业-禁止演绎4.0国际许可证
License: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
本文永久链接: https://blog.tmc1900.com/beijing-open/

SCM for git-repo

Git 复合工程的 SCM 计划方案

Author

  • Marcus Tang
  • tmc9031@gmail.com

开发模式

现状分析

现有的大型软件项目是由不同相对独立的模块组合而成,而不是单一的个体。而开发模式,一般是基于产品,即一个产品的项目开始和结束,通常会经过“软件资源的汇聚,修正,再稳定”几个过程。

然后,再一个新的产品需要推出,又要重复这一过程。当然,如果这一循环是相互无关的,那没有问题;但是,往往新产品是基于原有项目的提高改进,并且在同一时间新旧产品往往还需要同时维护,会导致软件资源的分离和不可控制。

如果,这个循环的次数越多,每次软件资源的汇聚,都代表着共有资源的分散,也就是说它们无法稳固在一个单纯的状态里以持续的改进,同时开发人员无法确切知道这些共有资源在不同项目产品之间的差异是什么,并且每次都在从头开始,降低了软件开发的效率

改进意见

总之,现有部署中一般是以开发的设备产品为单位建立软件仓库的,所以我们需要另一种方式,来提高共有软件资源的利用率。

基于以上讨论,可以从每个软件资源的角度进行汇聚,以达到网络设备由独立的模块组合而成的特点。

建议,引入开源的repo管理工具,把设备产品为单位的项目拆分为软件资源为单位的独立仓库,并用xml的标记方式把软件仓库组合为设备产品。开发人员就可以通过repo管理工具获取到设备产品的所有仓库集合。

以下是使用repo管理公共模块和独立模块组合成项目产品的开发模型:

公共模块:        c1    c2    c3
                |    /  \   |  \
                |   /    \  |   \
[项目产品]:      td-s     td-l    fdd
                |          |      |
独立模块:        s1        s2     s3

另外,repo需要与版本控制工具git进行配套使用。git与在使用的svn对比有一些使用上的差异,但是git有许多先进的特性,所以使用repo+git也是现有条件下最优的选择。

审核模式

现状分析

现有在软件开发的审核阶段(即code review),使用的是reviewboard的web服务

网络架构

软件仓库(SVN服务) <<---  reviewboard审核web服务
    |       ^            ^
    |       |           /
    |       |          /
    V       |         /
    开发人员(SVN工具)

开发人员把仓库的修改结果先提交到reviewboard上,leader再二次确认,就可以提交入库。但是,这个流程里有许多漏洞和问题:

  • rb与软件仓库并没有有实质上的关联,仓库的读写权限并不都它控制,也就是说开发人员也可以直接推送修改入库
  • rb不支持二进制文件的提交,开发人员只能采取直接推送入库
  • rb并不能保留Linux系统的其中一个文件属性“可执行”,实际开发中,常常发现修改结果没有达到预期都与该问题有关,并且解决方法也是直接推送入库(这也助长了开发人员绕过审核的可能)
  • rb根本不能保留svn merge的合并信息,并且svn status里add状态的文件,经过post-review命令不会产生补丁文件提交

改进意见

所以,为了提高开发中对软件仓库的监管,建议引入gerrit的web服务,它对于软件修改的审核是全面和强制性的。

开发人员的提交必须通过leader在gerrit的web上审核通过,才可以入库。以避免使用reviewboard审核服务过程中,一些弥补问题的中间操作给软件仓库和测试验证带来的影响。

整个改进方案

所需资源清单

  • Git版本控制工具
  • Repo仓库管理工具
  • Gerrit代码审核服务
  • Jenkins自动构建服务【可选】

部署架构

网络架构

Gerrit仓库+审核服务(sshd服务)  --->>   Git备份仓库【可选】
    |       ^
    |       |
    |       |
    V       |
开发人员(Repo+Git工具)

简要流程

  1. Gerrit服务器本身就保存着git仓库,同时可以再简单配置镜像服务器以备份git仓库
  2. 管理员通过webUI配置这些git仓库的访问权限
  3. Gerrit通过内置的sshd服务提供client端获取代码
  4. client端本地修改后推送至Gerrit服务器进行code review,通过后直接入库

部署优势

架构简单,可靠
集成了仓库管理,权限配置,代码审核并入库等功能

工作流对比

开发人员简要流程

初始化manifest.git清单库

$ repo init -b <branch>

依据manifest.xml清单来clone所有git库

$ repo sync

新建本地分支,开始工作

cd <work_dir>
$ repo start <branch> .

任务完成后,本地提交

vim <source_code>
$ git commit -a

推送review

$ repo upload

拉取更新update(与依据xml清单clone命令相同)

$ repo sync

迁移问题

版本控制工具对比

SVN

特点

To:self

目录树式的版本控制,基于最新version开发
version信息存储在每个子目录的.svn文件夹

To:developer

本地操作符合一般的思维,即:checkout版本,本地修改,checkin修改

问题

To:leader

只关心最新版本,但一般最新version是不稳定的,导致开发团队受到不可预知的干扰,影响任务进度
即便最新version看上去是”干净的”,但往往历史是通过添加和删除构成的,难以回溯
项目有必要的上游更新时,很难在最新version上合并改动,延误响应时效
使用的SVN的场景,新项目立项时,需要重用原项目的某些模块,很难导入它们的历史,只能是对应最新version的某一快照

To:developer

虽然允许子目录下svn update,但会使本地version处于一个无法确定的状态
如果任务有并发,本地修改的状态往往会相互干扰,分离困难
尤其review阶段,本地修改的状态要保持住,才能避免review不通过继续修改时受到其他代码改动干扰的情况,这样也阻碍了任务并发
常常无法确认的本地version状态,采取重新checkout版本的方法,时间代价高
协同开发切换branch时,开发人员一次删除本地version再重新检出,并回到工作状态就可能半天过去了
当svn的不同分支需要相同的提交时,只能手动修改再提交,并且一定时间后很难确认是否一个分支包含了某次改动,除非还是手动打开原文件确认

Git

特点

To:self

内容式的版本控制,基于repository历史开发
repository信息仅仅存储在一个项目根目录的.git文件夹
特有的stage暂存区,有利于抽出需要提交的差异,避免编译结果进入版本控制等

To:admin

创建和删除branch代价几乎为0,不会对repository服务器带来过多存储负担

To:developer

本地就是repository,可以游走于任意的版本
只有创建本地branch,可以分离不同任务的修改状态

问题

To:developer

操作灵活多样,起初使用时,可能并不清楚git工具对本地repository做了什么
本地repository操作时,要同时考虑到可能带来的影响,但这让repository变得可控

情景分析及工作流示例

注册用户

To:admin

gerrit采用http认证模式

管理员注册流程

$ htpasswd ~/review_site/etc/http.passwd admin
# 登录gerrit页面,添加ssh公钥,点击continue
$ ssh g2admin gerrit set-account admin --add-email admin@example.com --full-name admin@example.com

开发人员注册流程

[管理员操作]
$ htpasswd ~/review_site/etc/http.passwd user1
# 登录gerrit页面,直接点击continue,仅完成账号激活
$ ssh g2admin gerrit set-account user1 --add-email user1@example.com --full-name user1

[开发人员操作]
$ ssh-keygen    # 在本机生成ssh密钥和公钥
# 从管理员获取http账号密码,登录gerrit页面,添加ssh公钥,即可

新建仓库

To:admin

导入现有仓库

To:admin

仓库权限配置

To:admin+leader

Need:

一般的职能构成是一个部门由几个小组,没有小组有一位leader
需要配置哪些project只能由哪个小组可以访问,并且哪些project可以由几个小组可以访问
这些访问关系只能有一位部门管理员统一配置

Support:

项目层面采用access权限列表

支持project的权限继承自指定的ACL(未指定使用默认)
支持ACL之间的继承,特殊情况可以设置exclusive标志覆写继承的权限

用户层面采用group组管理权限

支持指定某些成员为一个group
支持设置group的所属关系,Owner可以添加删除组成员
支持设置group的包含关系,grpCD1,grpCD2等可以同时属于grpCD,然后grpCD和grpPD又可以同时属于grpALL

Example:

基本思路

指定多个user为group,在project的ACL里添加group的所需权限,避免为单独用户修改project的ACL
尽量不修改project的ACL,而是使用ACL的继承关系定义好一些预制的group,使权限需求相同的project继承自该ACL模板即可

admin可以把一个部门的开发人员定义为一个group,再在相关的project为该group打开read访问权限

配置core开发人员组的附加权限

Reference:     refs/heads/*
Create Reference
Push        # opensource only

Reference:     refs/tags/*
Push Annotated Tag

预配置权限的工程模板

$ cat groups
# UUID                                      Group Name
#
00ea643    leaderA
8495079    coreA
9163180    grpA

$ cat project.config
[submit]
    action = fast forward only
[access "refs/*"]
    owner = group leaderA
    read = group grpA
[access "refs/heads/*"]
    create = group coreA
[access "refs/tags/*"]
    pushTag = group coreA

邮件系统整合

To:admin

Need:

针对一组project设定email通知的用户组
开发人员upload提交后,自动触发一组project预配置好的用户组/用户进行review
另外,开发人员在必要的时候,也可以自定义email通知和review事件

Example:

email通知

工程级别的配置

$ vim project.config
[notify "leaderA"]
    email = group leaderA

review事件

开发人员手动添加review人员

开发人员自己upload提交后,添加leader组到”Need Code-Review”列表,然后点击”Publish Comments”,即可进入review人员的”Incoming reviews”列表

审核人员手动成为review人员

处于open状态的提交,任何人有权限进行review的,就会自动成为该次提交的Reviewer,并进入”Incoming reviews”列表

repo配合review

开发人员配置本机repo upload时,默认的review人员(仅uplaod时生效)

$ git config --global review.<review_url>.autoreviewer <lead_name>

开发人员每次repo upload时,手动指定review人员

$ repo upload --reviewers=<lead_name> [--cc=<other_email>]

开发人员已经upload后,通过ssh命令添加review人员(或者在web页面添加)

$ ssh g2user1 gerrit set-reviewers <Change-Id> --add <lead_name>|<group_name>

review与email

  • 提交者一旦添加review人员,就会发email通知该review人员,提交者Abandoned这次提交同样会通知该review人员
  • 该review人员的review动作,也会触发email给提交者和其他review人员
  • 默认review动作不会发email给自己,可以在”Settings -> Preferences -> CC Me On Comments I Write”勾选配置抄送给自己

获取仓库

To:developer

Example:

初始化repo清单列表,获取manifest.git库到当前目录的.repo目录下

$ repo init --manifest-url=ssh://user2@gerrit-vm:29418/project/manifest.git --no-repo-verify --repo-url=file:///home/gerrit/repo/git-repo --repo-branch=stable

获取repo清单列表上所有的git库

$ repo sync

更新repo清单列表,以下命令都会自动触发获取manifest.git库

$ repo init
or
$ repo sync

本地仓库开发

To:developer

Need:

一般针对单个git库的需要,repo都要能有对应的命令实现

  • 创建分支
  • 显示当前所处分支
  • 切换分支
  • 当前分支的修改与repo仓库对应分支版本的差异,类似status和diff
  • 当前分支的所有的超前提交与repo仓库对应分支版本的差异,类似指定diff的分支引用
  • 保留本地修改,拉取更新,类似fetch和pull
  • 回到repo仓库的某个tag状态
  • 显示repo仓库的不同tag之间的差异

Example:

repo sync结束后,默认没有本地branch,需要根据xml的标记来创建本地branch,开始工作

$ repo start <branch> --all

显示当前所处分支

$ repo branch
or
$ repo info

注意:避免分支杂乱的情况,可能以为在统一的分支,其实某个git库在一个其它的本地分支上
repo branch会显示所有的分支引用,所以命令返回的信息有重叠现象

切换分支

$ repo checkout <branchname> [<project>...]
or
$ repo forall [<project>...] -c git checkout <branchname>

注意:repo checkout只要有一个project检出成功就不会再提示error信息,所以更适合一开始就是repo start指定的project
repo forall -c可以有提示,但无法知道是哪个project,所以需要用repo forall -cpv

分别显示各个项目工作区下的文件差异,基于HEAD引用的提交位置

$ repo diff [--absolute] [<project>...]     # --absolute是去掉project路径
or
$ repo status [--orphans] [<project>...]    # --orphans是显示project以外的差异

当前分支的所有的超前提交与repo仓库对应分支版本的差异,基于跟踪的远程分支

$ repo overview [--current-branch] [<project>...]
or
$ repo info [--diff --local-only] [--overview [--current-branch]] [<project>...]

保留本地修改,拉取更新

$ repo forall -c git stash
$ repo sync
$ repo forall -c git stash pop

[CONFLICT]
$ repo status       # 查看冲突的project,大写字母"U"
$ cd <project>      # fix ...
$ git stash drop    # 手动清理冲突时没有删掉的临时区

回到repo仓库的某个tag状态

$ repo init -b <tag_branch>
$ repo sync --local-only [--detach]

显示repo仓库的不同tag之间的差异

$ repo forall -c git diff --stat refs/tags/<tag_name>
or
$ repo diffmanifests manifest1.xml [manifest2.xml] [--raw]

推送提交

To:developer

本地修改,局部分支法

$ repo init -b <fdd-dev>
$ repo sync --detach

$ cd fdd

$ repo start <fdd-dev> .    # NOTE: symbol "." mean current dir
$ repo branch
*  fdd-dev                   | in fdd
or
$ git co -b <fdd-dev> remotes/m/fdd-dev
$ git branch -vv
* fdd-dev 4c5d518 [gerrit/fdd-dev] Merge branch 'fdd-rel' into fdd-dev

$ vim ...
$ git ci -a
    up: make c02

$ repo upload

不等review完成,直接继续同一git库的其它任务

$ repo start <fdd-dev-b> .    # NOTE: symbol "." mean current dir
$ repo branch
 P fdd-dev                   | in fdd
*  fdd-dev-b                 | in fdd
or
$ git co -b <fdd-dev-b> remotes/m/fdd-dev
$ git branch -vv
  fdd-dev   ef9c17e [gerrit/fdd-dev: ahead 1] up: make c02
* fdd-dev-b 4c5d518 [gerrit/fdd-dev] Merge branch 'fdd-rel' into fdd-dev

$ vim ...
$ git ci -a
    up: make c03
$ git branch -vv
  fdd-dev                ef9c17e [gerrit/fdd-dev: ahead 1] up: make c02
* fdd-dev-b              c132ecc [gerrit/fdd-dev: ahead 1] up: make c03

等到上次提交的review完成,那么当这次需要upload时,先sync一下

$ repo sync
project fdd/
: git rebase --onto ef9c17e c132ecc^1
First, rewinding head to replay your work on top of it...
Applying: up: make c03

$ git branch -vv
  fdd-dev                ef9c17e [gerrit/fdd-dev] up: make c02
* fdd-dev-b              5922080 [gerrit/fdd-dev: ahead 1] up: make c03

$ repo upload

清理已经review通过的分支

# 先确认所有分支已经repo upload,即显示分支状态为大写字母"P"
$ repo branch
 P fdd-dev                   | in fdd
 P fdd-dev-b                 | in fdd

# 安全清理prune
$ repo prune

# 回到无分支状态
$ repo branch
   (no branches)

注意事项

一个分支一次repo upload的提交历史最好只用1个,最多也不要超过3个,由于gerrit需要逐个review
同一分支,永远不要用git pull拉取更新,即在本地用merge合并分叉的远程branch来更新,对于远程来说主线HEAD^会倾斜到本地还未push的提交

提交过合并的节点后,更不可以rebase后再push

Rei总结

1) 代码提交者身份

向远程分支提交代码时,先 git pull –rebase,避免把本地状态当作分支提交。

2) 分支管理者身份

进行线上分支合并时,一律使用 git merge –no-ff,保留合并时间戳。

代码审核

To:developer+leader

Submit Type:

  • Fast Forward Only 严格的快进,合并只在本地完成
  • Merge If Necessary 一般快进,不然就合并
  • Rebase If Necessary 一般快进,不然就换基

服务端的自动Merge类型对于独立的分叉提交可以很好的合并,方便提交者更新本地,但历史不够线性
服务端的自动Rebase类型对于独立的分叉提交可以很好的换基,历史线性,但不方便提交者更新本地

优先review合并提交,并从合并的依赖提交依次review;否则合并提交又因为出现分叉而无法快进时,服务端不管采用哪种Submit类型都无法很好解决,merge会产生多余的合并节点,rebase会丢失合并节点

持续并行的特性分支,如果需要主动upload一个merge –no-ff的合并节点,建议采用Merge类型,并且review时从依赖提交开始,当已经由于分叉通过服务端自动合并,就abandon提交上来的合并节点即可
不过,服务端的自动Merge类型的节点是没有Change-Id也没有显示在评审列表里的,如果需要严格通过本地创建合并节点,还是推荐采用Rebase类型,以保持历史线性
另外,持续并行的特性分支情况,还是需要新建branch,再在需要的时候通过本地主线merge特性branch生成的合并节点来upload(测试下来先submit分叉提交会有问题,还是会自动merge,所以只能牢记先review合并提交,不然不得不用ff-only类型?)

自动构建

To:developer+leader

版本发布

To:developer

To:leader

[repo发布流程]

基于release分支创建tag

$ repo init -b fdd-rel
$ repo sync --detach
[Test OK]
$ repo forall -cpv 'git tag -a fdd-v1.0 -m "release fdd-v1.0"'
$ repo forall -cpv 'git push gerrit --tags'

合并回develop分支

$ repo checkout fdd-dev
$ repo forall -cpv 'git merge --no-ff fdd-rel'
$ repo forall -c 'git ci --amend'   # NOTE: must operation
$ repo upload

[manifest发布流程]

大版本发布

$ git co fdd-rel
$ git co -b fdd-v1.0
$ vim default.xml
    revision="refs/heads/fdd-rel" --> revision="refs/tags/fdd-v1.0"
$ git ci -a
$ git push origin HEAD:fdd-v1.0

$ git tag -a "fdd-v1.0" -m "release fdd-v1.0"
$ git push origin --tags

小版本发布

$ git co fdd-v1.0
$ git co -b fdd-v1.1
$ vim default.xml
    revision="refs/tags/fdd-v1.0" --> revision="refs/tags/fdd-v1.1"
$ git ci -a
$ git push origin HEAD:fdd-v1.1

$ git tag -a "fdd-v1.1" -m "release fdd-v1.1"
$ git push origin --tags

版权声明

作者: Marcus Tang
许可证: 创作共用保留署名-非商业-禁止演绎4.0国际许可证
License: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
本文永久链接: https://blog.tmc1900.com/scm-repo/