git与phabricator

本文摘录自本人的《Git聊天入门》

arc 为何物

  • arc 全称是 arcanist,百度给出的中文翻译是:巧匠。
  • arc 是Facebook的Phabricator系统中用户端的命令行工具,配合pha提交变更评审的。
  • arc 的安装还有点折腾,要先在本机安装PHP,和一个php的工具集:libphutil
    • windows的安装后,目录下包括下面3个主要部件:
    Phabricator/--arc/--arcanist
                |     |-libphutil
                |-xampplite-win32-1.7.3
    
      + arcantist是arc的主程序:https://github.com/phacility/arcanist.git
      + libphutil是php的工具集:https://github.com/phacility/libphutil.git
      + xampplite是apache+php+mysql+perl的一个打包,160+M,要知道phabricator整个才180+M。
    
  • arc 包含很多子命令,
    • arc help:列出来子命令看看
    • arc diff:调用svn diffgit diff生成差异并发送给phabricator生成评审单
    • arc list:列出当前peding的revision —— revision要说明一下:pha生产的每个评审单都对应一个revision,可以理解为svn/git的一次提交,但又不在用户的svn/git上体现,可以理解为pha上也驻留这一个svn/git,来存储评审单信息,每单对应一个revision。
    • arc patch:将pha上的revision变更patch到本地工作拷贝上
    • arc amend:更新git commit的message,即:可以把pha上某个revision的message应用到本地git的某个commit上
    • arc commit:svn专用,pha上评审完毕后,将本地的变更做svn commit
    • arc land:git专用,pha上评审完毕后,将本地分支做git push,所以需要有 origin
    • arc lint:静态代码分析,不要以为arc只是生成评审单的,它还内嵌了一堆的lint工具,python的、java的、js的……五花八门,在Phabricator\arc\arcanist\src\lint\linter\__tests__这个目录下列出了这些lint工具
    • arc unit:执行单元测试,这个就需要用户自己来指定单元测试工具了
    • arc close-revision:使用arc关闭某个revision,而不必上pha上鼠标点点点啦
    • …… 还有n多,不一一列举了,头晕
  • arc 的 configuration
    • 和git类似,git有git config --[global/system/local] xxx ...,arc也有arc set-config --[user/local] xxx ...
    • 和git类似,git查看config有git config -l,arc也有arc get-config

arc的安装和配置

Windows下的安装

Ubuntu下的安装

  • 安装
    • sudo apt-get install php5 php5-curl
    • cd somewhere //arc的安装目录
    • git clone https://github.com/phacility/libphutil.git
    • git clone https://github.com/phacility/arcanist.git
    • sudo ln -s arcanist/bin/arc /usr/local/bin/arc
    • vi ~/.bashrc
      • source $somewhere/arcanist/resources/shell/bash-completion
  • 配置
    • arc set-config $pha-server //eg: arc set-config http://pha.etz.com.cn
    • arc install-certificate
      • 到 $pha-server 上查找帮助,找到tocken,填到这里
      • 注意proxy的屏蔽

arc diff 初步

  • SVN中,arc diff会把未提交的本地工作拷贝中的变更生成评审单,执行arc diff之前不需要也不能执行svn commit,最终评审完,用arc commit来代替svn commit
  • git中则完全不一样,arc diff <startCommit>之前需要首先git add&git commit如果本地工作拷贝中有变更,arc diff会自动替你add和commit,因为arc diff是把git中两个commit之间(即:一个range)的变更提交到pha上生成评审单,所以问题来了:两个commit节点是如何指定的?
    • 两个commit节点是:startCommit 和 HEAD
    • startCommit如果缺失,则默认使用 git merge-base origin/master HEAD
      • 这又是个啥东东?git help merge-base,意思是找到 origin/master 和 HEAD 之间的最近祖先节点。
      • git help merge-base中有几个例子,其中一个是:
           o---o---o---B
          /
      ---o---1---o---o---o---A
      
        * `git merge-base A B `将返回节点1,好好体会一下,呵呵。
      
      • 所以为了不出乱子,最好自己指定 startCommit
  • arc diff需要填写一些信息,所以执行过程中会跳入到一个编辑器中,windows版的arc会打开一个简陋的窗口,ubuntu版的arc就直接打开默认的编辑器(如vi)了。需要填写的信息有:
    • Test Plan - 必填,详细说明你的测试计划;
    • Reviewers - 必填,审查人的账户,多个使用","隔开;
      • 在ubuntu下,用vi编辑此信息时,不会自动不全人名,则需要到phabricator网站上的搜索窗口,找到需要的人,把TA的账号写在此处
    • Subscribers - 非必填订阅人,多个使用","隔开。

实战一下:

  • 创建一个temp的git repo
10036143@A20939270 MINGW32 /f/temp (master)
$ git log
 *  75c616b | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create
  • arc diff
    • 会提示错误,没有指定 origin/master,因为默认startComiit是git merge-base origin/master HEAD
  • arc diff 7584
    • 可以创建评审单的,因为是拿 HEAD(即75c6)与7584比较
    • git show HEAD 可以查看 HEAD 指向哪个节点
  • arc diff 7584 --preview
    • 可以在pha上创建评审单,但跳过指定评委等步骤,单子已经在pha上有了,可以先看看,后续在pha上慢慢指定评委等
    • 这是专门给处女座准备的啊
  • 有一点需要说明:
    • arc diff会根据工作拷贝的相关信息(比如 path, branch name, local commit hashes, and local tree hashes)来自动创建和关联一个pha上的revision,这让一些掌控欲比较强的人可能有些恼火,可以手工指定
      • arc diff --create <startCommit>:在pha上创建一个新的revision
      • arc diff --update Dxxxx <startCommit>:在pha上一个已有的revision(编号Dxxxx)上做增量

arc diff 为什么把我已有的commit log修改了

在上面的步骤中有一个奇怪的地方:执行完arc diff xxxx后,原有的HEAD节点被arc重新创建的一个节点所替代

  • 执行arc diff 7584后,75c6被替代为了26c0
$ git l
 *  26c0efc | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create
  • 再次执行arc diff 7584后,26c0被替代为了e6db
$ git l
 *  e6db93c | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create
  • 再次执行arc diff 7584后,e6db被替代为了7c29
$ git l
 *  7c29204 | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create
  • 但其实75c6、26c0、e6db都还是存在的,git show可以看到
$ git show 75c6
commit 75c616b3a6de15e7004c231486a91e338ae023a6
Author: wkevin <wkevin27@gmail.com>
Date:   Wed Jun 8 15:55:19 2016 +0800

    hah

事情变得很蹊跷,arc为什么要新建一个commit呢?

下面再来验证一下:如果本地有modified(待add)或stagging(待commit)文件的话,arc diff是不是也会新建一个commit呢?

  • 当前状态
$ git l
 *  1cce5be | 2016-06-08 16:05:27 +0800 |  wkevin  neww
 *  7c29204 | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create
  • 做一些有本地修改,但不 git commit -a
  • arc diff HEAD^,会首先把未提交的变更进行提交,并且更新(amend)当前commit的message,然后向已有的revision进行update
$ arc diff HEAD^
You have uncommitted changes in this working copy.
  Working copy: F:\temp\
  Unstaged changes in working copy:
    README.md
    Do you want to amend this change to the current commit? [y/N] y
Linting...
No lint engine configured for this project.
Running unit tests...
No unit test engine is configured for this project.
SKIP STAGING Unable to determine repository for this change.
Updated an existing Differential revision:
        Revision URI: http://pha.zte.com.cn/D30449
Included changes:
  M       README.md
  • 1cce5be 又被 20ae4c5 替代了,而不是在 1cce5be 的基础上新建一个commit
$ git l
 *  20ae4c5 | 2016-06-08 16:05:27 +0800 |  wkevin  neww
 *  7c29204 | 2016-06-08 15:55:19 +0800 |  wkevin  hah
 *  7584e84 | 2016-06-08 15:55:01 +0800 |  wkevin  create

为了解开这个谜团,我们来跟踪一下arc diff的操作

arc diff --trace <startCommit>

摘录一部分打印:

>>> [1] <http> http://pha.zte.com.cn/api/user.whoami
>>> [2] <exec> $ git diff --no-ext-diff --no-textconv --raw 'HEAD' --
>>> [3] <exec> $ git ls-files --others --exclude-standard
>>> [4] <exec> $ git diff-files --name-only
>>> [6] <exec> $ git rev-parse 'HEAD'
>>> [7] <exec> $ git merge-base 'f8c1' 'd6efce6e8804ecb027762e0151ed071bc7d63b6d'
>>> [8] <exec> $ git log --first-parent --format=medium 'f8c101daaf75121dd4f1f1380b4dc5c1ed85cea0'..'d6efce6e8804ecb027762e0151ed071bc7d63b6d'

首先到phabricator服务器上验证tocken,并根据 startCommit 做出一些判断

>>> [16] <event> diff.willBuildMessage <listeners = 0>
>>> [17] <conduit> differential.getcommitmessage() <bytes = 295>
>>> [18] <http> http://pha.zte.com.cn/api/differential.getcommitmessage
>>> [19] <exec> $ git symbolic-ref --quiet HEAD
>>> [20] <exec> $ which 'editor'
>>> [21] <exec> $ editor  '/tmp/edit.cjol8q3bi1sg0kwk/new-commit'

然后到phabricator服务器上创建一个单,并根据pha的请求,打开editor,编辑评审单的信息

>>> [22] <exec> $ git commit --amend --allow-empty -F '/tmp/8qihi3x4l2ww4o8w/10039-Vbjrxm'

关键是这里了,无条件的更新了当前 HEAD 节点的 message。

其实 git commit --amend 的官方help中是这样解释的: Replace the tip of the current branch by creating a new commit.

这样arc diff <startCommit>步骤就明朗了:

  1. 提示用户填写评审单信息(Test Plans、Reviewers、Subscribers……),然后使用这些信息 git commit --amend 到当前分支的 HEAD 节点
  2. 新的节点(即:新的HEAD) 成为 endCommit
  3. 再拿 HEAD(即endCommit)与 startCommit 执行 git diff,输出的内容提交到 pha

arc 为什么要这么做?为什么要“玷污”我的现有节点?如果这个节点是其他分支的基础节点怎么办?…… —— 这个事情可以这么看:arc diff只是新建了一个commit,用来存储评审单的相关信息,并且把当前分支的HEAD指向了新建的commit,想好了这一点,事情其实很好办,下一节我们来规避它。

如何避免arc diff玷污现有节点

创建专用于评审的分支

  • git branch review
  • git checkout review
  • arc diff <xxx>arc diff --preview <xxx> //创建评审单或预审单(到pha网站上进行下一步的操作,可用于ubuntu下不能自动补全人名的环境)
  • git checkout master
  • git branch -D review //评审单一旦创建,review分支就没有存在的必要性了

如何创建只包含部分文件的评审单

可能只希望评审方案文件(假设: design.md),但commit中包含相关的图片、svg、等文件,不需要提交到pha,如下处理:

  • git branch review <oneOldCommit> //从 design.md 创建或修改前的节点创建一个分支
  • git checkout review
  • git checkout master design.md //将master分支上的 design.md check 到 review 分支
  • git commit -am "review for design.md"
  • arc diff HEAD^arc diff --preview HEAD^
  • git checkout master
  • git branch -D review

推荐阅读更多精彩内容