Git最佳实践与常见问题

最佳实践

最佳实践之:如何安全地避免冲突

大家在小组协同使用 Git 和 Gerrit 时,经常会遇到因为他人在自己之前提交了代码,
自己提交时因为代码冲突需要解决后才能提交的问题。这其实和 Gerrit 没有关系,单独用
Git 也会遇到同伴在自己之前提交的问题。

一个比较笨但比较好用 git 使用模式如下:

本地 git clone 一个 develop 分支,仅用作和远端 Git 库同步(不要在上面开发),
即仅在这个本地 develop 分支上执行 pull 操作,不要和本地其它分支做 merge 。

本地其它分支,用作开发、review、 入库,即作为开发分支,这些开发分支创建时基于本地
最新 develop。开发分支入库后,如果不需要继续开发就删除它, 再重建其它 feature 的
开发分支(仍基于最新的 develop)。

当大家发现通过 review 后,却不能提交代码时,一般是产生了冲突,因为自己的好伙伴
手好快先于自己提交了(肯定练过)。

这个时候 checkout 到 develop 上,pull 一下最新 develop 代码。 然后再切换到当前
开发的分支上, 执行 git rebase develop, 若有冲突解决冲突, 没有冲突,rebase 完后
(add, and rebase –continue), 再发 review(新的 patch set,用来解决冲突的), 一般
就可以过了。

最佳实践之:小量修改、时刻提交

基于 Gerrit 的 Review 工作流,提倡每个 Change 都是容易 Review 的。这意味着每个 Change
的大小要比较小,小到什么程度呢?大家可以参考 Web GUI 中的 Size 一栏的长度与颜色:

  • 绿色:表示足够小,是可 Review 的,大家阅读起来很舒服
  • 黄色:表示不够小,Review 起来可能有点累
  • 红色:表示变更很大,基本上是不可 Review 的

我们尽量避免出现黄色与红色的,这就是小量修改的原则。

时刻提交说的是:一次 commit 并且 fetch 远程的变更并且合并到本地分支之后,马上进行一次
git push origin HEAD:refs/for/{BRANCH} 的操作。不要进行多次 commit 之后才 push

时刻提交的好处是减少冲突、减少合并提交产生 Change-Id 无法生效的问题,并且出了问题容易解决。

最佳实践之:PUSH 简写,并指定 Reviewer

大家使用 git Push Review 的时候,可能很不习惯这一部分:

git push origin master:refs/for/master

这条命令的分支,称为魔术分支,并不是真实存在的,仅用于 Gerrit 做 Review。

如果大家的分支工作流程可以指定只用一个分支来 Push Review,就可以使用本条最佳实践:

cd /path/to/your/repository
vim .git/config

修改 git config 文件的远程仓库 origin 部分:

[remote "origin"]
    url = ssh://{MY-USERNAME}@teamcode.fangdd.net:29418/{PROJECT-NAME}
    fetch = +refs/heads/*:refs/remotes/origin/*
    push = HEAD:refs/for/{BRANCH}

这样,你就可以简单地使用

git push origin

来 Push 你的代码给大家 Review 了。

如果你想让某些同伴来 Review 你的代码,你可以使用 r 参数来添加 Reviewer:

[remote "origin"]
    url = ssh://{MY-USERNAME}@teamcode.fangdd.net:29418/{PROJECT-NAME}
    fetch = +refs/heads/*:refs/remotes/origin/*
    push = HEAD:refs/for/{BRANCH}%r={SOMEONE}@fangdd.com,r={SOMEONE}@fangdd.com

这样,当你 git push origin 后,Reviewer 就能收到邮件通知了。

最佳实践之:分支合并之 merge vs rebase

分支合并的最好的工具,应该是 merge,例如

# 将本地的 develop 分支合并到 release 分支
git checkout release
git merge develop

# 将远程 origin 的 develop 分支合并到本地的 develop 分支
git fetch origin develop
git merge origin/develop
或者
git fetch origin/develop

使用 merge 来合并分支,会无条件地保留所有的提交记录。

rebase 是 git 提供的用于将提交记录重演的工具,这种重演也叫变基,通俗地说,就是回到某个
历史节点上,让历史重新演进。rebase 就是一个月光宝盒。

由于 rebase 可以将一个分支上的历史在另一个分支上重演,因此常常备大家用作分支合并的另一
选择。rebase 会在重演的时候变更提交记录的 id(sha-1),本质上不是用来作分支合并的。

在 gerrit 提供的 git 服务中,rebase 可能在不当的重演过程中,产生两个问题:

  • 一是可能是重演时产生新的提交或除 id 不同其它完全相同的重复提交,并丢失或不能生成 Change-Id
  • 二是可能产生除 id 不同其它完全相同的重复提交,由于 Change-Id 相同而无法 push。

因此,在使用 gerrit 作为 review 工作流的实践中,请:

  • 使用 merge
  • 少用或不用 rebase

关于 rebase 的使用,可以

git help rebase

不了解 rebase 的原理而滥用,会造成不少难以理解的问题,但如果已经了解 rebase,活用 rebase
会带来很多的好处(所谓好处,就是你能感受到的便利)。

关于 merge 与 rebase 的讨论,可以参考 git book: Git-分支-变基

最佳实践之:SSH - 使用主机别名

这个实践,可以让你使用一个很短的名字,代替 {MY-USERNAME}@teamcode.fangdd.net:29418,
也可以使用指定的密钥来进行服务器认证。

vim ~/.ssh/config

加入以下内容

# 这一节配置的目的,是让 teamcode 代表 {MY-USERNAME}@teamcode.fangdd.net:29418
Host teamcode
Hostname teamcode.fangdd.net
User {MY-USERNAME}
Port 29418
# 如果你有多个密钥,下面一行可以指定私钥
#IdentityFile /path/to/your/id_rsa

保存退出

经过以上配置,对于下面的地址

ssh://{MY-USERNAME}@teamcode.fangdd.net:29418/fangdd/engineering/teamcode

等价于

ssh://teamcode/fangdd/engineering/teamcode

它的优势在于获得一个与 username, host, port 无关的地址,而且更短。这在需要统一的地址
引用的场景中,尤其重要。

常见问题之:合并提交造成 Change-Id 无法生成

当远程仓库的代码有更新时,如果你使用

git pull

或者

git fetch origin {BRANCH}
git merge origin/{BRANCH}

来合并远程的变更的时候,会产生 4 种结果:

  1. 远程无更新
  2. Fast Forward,无新的变更(no new changes)
  3. 合并提交,有新的变更,多数情况无法自动生成 Change-Id
  4. 合并冲突,手动解决冲突后再提交,能顺利生成 Change-Id

问题在于第 3 种结果,是大家最容易遇到并且很困惑的。你可以马上使用

git commit --amend

来尝试这次的合并提交生成 Change-Id。

对 git 熟悉的并且精通脚本的同学,可以在 prepare-commit-msg 上作文章,但
可能会产生副作用,不建议使用!

最好的解决方法还是遵循《最佳实践之:小量修改、多次提交》的流程。

常见问题之:合并分支后,push 时提示 no new changes

假如在 develop 分支开发,提测时,将 develop 合并到 release 分支,如果合并的结果是
Fast Forward,那么 git push origin HEAD:refs/for/release 就会提示 no new changes
错误,无法 push。

原因是在 Fast Forward 情况下,并没有新提交,没有新的变更,仅仅是将 release 的指针指向了
develop,而此时 develop 所指的提交已经是最新的了。

推荐大家维护一个叫 release-notes.md 的文件,从 develop 分支合并到 release 分支之后,
在 release 分支上修改 release-notes.md 文件,说明本次提交的主要内容。

这个文件的内容的组织,可以参考 git for windows 的 release

由于修改了 release-notes.md,有了新的变更,就可以 git push origin HEAD:refs/for/release
了。由 release 分支向 master 分支合并,也是一样的做法。

产生这个问题的原因有多种,导致 commit-msg 钩子没有执行,下面是 3 个常见原因

  1. 克隆项目的时候,没有下载 commit-msg 钩子。
  2. 在分支合并的时候,产生了合并提交,git 无法执行 commit-msg 钩子
  3. 在 rebase 或 cherry-pick 的时候,无法执行 commit-msg 钩子

第 1 个原因,解决办法请参考工作流

第 2 个原因,参见《常见问题之:合并提交造成 Change-Id 无法生成》

第 3 个原因,参见《最佳实践之:分支合并之 merge vs rebase》

如果缺失 Change-Id 的提交是最新的一次提交,只需要执行

git commit --amend

保存退出即可。

假如最新 3 次提交记录如下

# 此历史列表由以下命令得出
# git log -3 --pretty=format:"%h %s"
6d3d39b 修复团购按钮错位 1px 的Bug
11f0806 Merge origin/develop into develop
c8a32de 修复用户无法注册的Bug

其中的 11f0806 缺失了 Change-Id,需要补上,有多少办法,不同的办法解决于你不同的意图。

办法一:reset。reset 最适用于修改第 2 条提交记录。

# 将 11f0806 以后的提交全部放弃,其后的变更从 git 索引区回到工作目录
git reset 11f0806
git commit --amend
# 将 reset 影响的文件重新加回到索引区
git add .
git commit --m "修复团购按钮错位 1px 的Bug"

办法二:rebase。rebase 比较通用,可以修改多次之前(历史比较远)的提交。

# 我们要修改 11f0806,所以历史回到 c8a32de
# 也可以用 git rebase 11f0806^
git rebase c8a32de

在弹出来的编辑器中,将 11f0806 前面的 pck 改为 edit

edit 11f0806 Merge origin/develop into develop
pick 6d3d39b 修复团购按钮错位 1px 的Bug

保存退出后,历史定格在 11f0806,等待你的篡改^_^

git commit --amend

改完后,让历史继续前进,直到现在为止

git rebase --continue
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值