Git, Gerrit, Hudson (2) -- 使用GIT
1. 拷贝远程库
在 Git bash 中,使用以下命令拷贝远程库:
git clone review:<repository_name>
<repository_name> 是远程库的名称,例如:远程库叫 "ProjectA",使用以下命令:
git clone review:ProjectA
但是,如果使用的是 Tortoise 将不能使用这种简写的形式,得使用 URL 链接:
ssh://<username>@<Gerrit_host_name>:29418/ProjectA
注意:以上步骤并不能把 ProjectA 直接下下来,而是建立了连接,后面还需若干步骤来下载代码。
2. 建立“钩子”
你需要取得 Gerrit 的 Change-Id 的钩子(hook)。使用 Git bash 输入以下命令:
# 进入到刚才拷贝下来的远程库目录 cd ProjectA # 从 server 上下载 “钩子” scp -p review:hooks/commit-msg .git/hooks/
一个叫 "commit-msg" 的文件会被下载下来。
hooks 实际上是包含一系列命令的脚本,该脚本在 commit 时会被自动调用,你可以在服务器上放置这样的脚本,以统一客户端的提交行为 (或者其它需要统一的事情 )。
3. 可视化组件
git 有一个可视化工具:gitk,它可以展示每次的提交,也可以看到哪些被 merge ,以及 branch 上的情况,在命令行下输入以下命令:
# 显示当前所在分支( Branch )上的所有提交 gitk # 显示所有分支的所有提交 gitk --all
4. 创建跟踪分支(Tracking Branches)
在第 1 步中的 git clone 命令会自动在本地创建一个叫 'master' 的分支跟踪远端库中的 master (即 'origin/master' )。而后可以使用 git branch 在本地创建分支来跟踪服务器上的远端分支( Remote Branches )。我们称这种本地分支为 Tracking Branch 。创建跟踪分支的命令如下:
git branch <tracking_branch> -t origin/<remote_branch>
例如:
git branch 1.0.0 -t origin/1.0.0
以上命令为:在本地创建 1.0.0 的分支来跟踪远端 1.0.0 分支( 即 'origin/1.0.0 )。然后,可以使用 checkout 命令将当前目录切换到 1.0.0 上去。
git checkout 1.0.0
以上两步可以合并为一步,创建 Tracking Branch 同时切过去:
git checkout -b 1.0.0 -t origin/1.0.0
注意:与 SVN 不同,checkout 在 git 中不是签出,而是切换到指定分支。签出是 git pull 。
5. 从 server 上更新
如果要从 server 上签出更新。首先,切换到想签出的分支上(如果当前不在该分支上),然后签出:
git checkout 1.0.0 git pull
git pull 针对于 Tracking Branch,对特征分支( Feature Branch ),需使用 git rebase 。
6. 创建特征分支( Feature Branch )
无论何时要对代码进行修改,总是应该先创建特征分支,然后在特征分支上进行修改。
首先,切换到跟踪分支上( 如果当前不在该分支上 ),签出更新:
git checkout 1.0.0 git pull
然后,在该跟踪分支上创建特征分支并切换到上面:
git checkout -b <feature_branch>
例如:
git checkout -b FixForBug1922
此处创建了一个叫 'FixForBug1922' 的特征分支。特征分支的应该总是取有意义的名字,最好能够概括本次修改的目的。远端服务器并不知道这个特征分支的存在,所以如果要对它进行更新,需要使用 git rebase 命令。
7. 备份特征分支
特征分支存于本地,如果需要把它备份到服务器上,使用如下命令:
git push origin +FixForBug1922:features/abc/FixForBug1922
该命令会将特征分支 'FixForBug1922' 备份至服务器,并称之为 'features/abc/FixForBug1922' 。
注意:此处的 '+' 号。在你 rebase 或 修改了本地代码后,希望再次备份时会产生作用。因为这意味着需要抛弃以前提交的备份,服务器需要知道你确切地知道自己在做什么。如果是第一次备份或没做任何修改,则不需要 '+' 号。
如果你要删除某特征分支,也可以选择相应的删除远端的备份:
# 删除本地的特征分支 git branch -D FixForBug1922 # 删除远端的对应备份 git push origin :features/abc/FixForBug1922
注意:删除远端备份也使用 push ,只不过这次将空 push 到了远端。
8. 提交文件
提交分为两步。首先,将要提交的文件加入( add )到临时区域( staging area )。这使你可以选择性提交( git 甚至允许只提交文件内的某些修改,参见 Pro GIT )。与 SVN 不同,无论对修改的还是新增的文件,都需要执行 add 命令。对 EGit 和 Tortoise 来说,当你选者文件并 commit 的时候,add 会被自动执行。
git add file1 git add file2 git commit
如果你十分有把握,目前所有的变化都是要提交的,可以使用一句话的简写:
git commit -a
注意:commit 只是提交到本地库。签入到 server 还需要其它步骤。下一步将看到如何修改提交。
9. 修改/撤销提交
如果是在同一个工作任务中,最好是修改以前的提交。这样在 code review 时因为只有一次提交需要审查,可以减少审查者的工作量。甚至在 review 以后,如果修改了代码也要修改前一次的提交( 针对同一次任务 )。因为此时你的修改会被放在同一提交中被 review,所有以前的 comments 都会被保留下来,并且还能比较前后的异同。
git add file1 git add file2 git commit --amend
如果要恢复已修改(但还未添加add)的文件,使用git checkout恢复:
git checkout -- <file_name>
如果要撤销已添加(但还未提交commit)的修改,使用git reset恢复:
git reset HEAD <file_name>
如果要撤回已提交(commit)的修改,使用git revert恢复:
# 恢复到最近一次提交 git revert HEAD # 恢复到指定版本 git revert <commit_id>
10. 重新放置( rebase )特征分支
rebase 会将你的修改重新放置到一个更新的代码基础上。更多详情参见 Pro GIT。
首先,从 server 上更新跟踪分支:
git checkout 1.0.1 git pull
然后重新放置特征分支:
git checkout FixForBug1922 git rebase 1.0.1
以上命令首先切换到特征分支 'FixForBug1922',然后将该特征分支上新的提交( commit )应用到 1.0.1 上去。
有可能会有冲突,如果需要,合并冲突,然后:
git commit git add conflicted/file/1 git add another/conflicted/file git rebase --continue
11. 签入代码,进行 review
代码提交完成后就可以上传代码至 Gerrit 并进行 review。Gerrit 是一款 review 工具,同时它也是中心库。命令如下:
git push origin FixForBug1922:refs/for/1.0.1/FixForBug1922
注意:如果在 master 上签入,则为:
git push origin master:refs/for/master
- "git push origin": 告诉 git 上传更改至 server 上的某个 branch 。
- "FixForBug1922:refs/for/1.0.1/FixForBug1922": 告诉 git 将本地的 'FixForBug1922' 的更改提交到服务器上 'refs/for/1.0.1/FixForBug1922' 分支上去。
如果我们不需要 review ,可以将代码提交到 'refs/heads/1.0.1' 分支上。
- "refs/for/1.0.1": 这告诉 Gerrit 有更改需要被 review ,如果 review 通过,代码将被合入到分支 '1.0.1' 中去。
- "/FixForBug1922": 这是一个可选标签,它能将一系列的 review 组到一起( 如果它们确实应该分在一起 )。
注意:你的特征分支是从哪个分支创建的,就要提交到哪个分支上,否则 Gerrit 会认为你想把一个分支上的所有提交都合并到另一个分支上。
12. 向上合并更新
如果要避免一次艰难痛苦的合并,最好的办法是经常合并。
我们应该避免只更新部分版本,并且最好是整个分支的进行合并。基于这个理由我们应该总是向上合并而不是向下合并。
例如:你可以合并整个 '1.0.1' 分支到 'master' ,因为 'master' 应该总是包含了 '1.0.1' 分支中的所有更改,而不应该从 'master' 到 '1.0.1' 分支,因为 'master' 中可能包含了很多不稳定的代码。( 假设 '1.0.1' 已经是一个稳定版本 )
所以,在开发中应该遵循以下原则:
- 经常合并。最理想的情况是每一个稳定的提交都进行合并。
- 如果需要更改( 例如需要修正一个 bug ,该 bug 是先前的分支就有了只是现在才发现 )。从尽可能早的版本开始更改,合并。
- 沿着版本号挨个向上合并。例如:合并从 1.0.1 -> 1.0.2 -> 1.0.3 -> master ;而不是直接从 1.0.1 -> master 。
- 不要挑着更改,合并。
合并命令如下:
# 切换到目标分支 git checkout master # 创建并切换到特征分支 git checkout -b toMergeBugFix1922 # 执行合并 git merge 1.0.1 # 如果出现冲突,解决冲突,然后: # 1. 使用 add 命令告诉 git 我们已经解决了冲突 git add conflictedfile.txt git add conflictcode.java # 2. 提交合并 git commit # 将合并提交 review git push origin toMergeBugFix1922:refs/for/master # 让某些人来做 review