Git 使用规范流程

作者: 阮一峰

日期: 2015年8月 5日

团队开发中,遵循一个合理、清晰的Git使用流程,是非常重要的。

否则,每个人都提交一堆杂乱无章的commit,项目很快就会变得难以协调和维护。

下面是ThoughtBot 的Git使用规范流程。我从中学到了很多,推荐你也这样使用Git。

第一步:新建分支

首先,每次开发新功能,都应该新建一个单独的分支(这方面可以参考《Git分支管理策略》)。


# 获取主干最新代码
$ git checkout master
$ git pull

# 新建一个开发分支myfeature
$ git checkout -b myfeature

第二步:提交分支commit

分支修改后,就可以提交commit了。


$ git add --all
$ git status
$ git commit --verbose

git add 命令的all参数,表示保存所有变化(包括新建、修改和删除)。从Git 2.0开始,all是 git add 的默认参数,所以也可以用 git add . 代替。

git status 命令,用来查看发生变动的文件。

git commit 命令的verbose参数,会列出 diff 的结果。

第三步:撰写提交信息

提交commit时,必须给出完整扼要的提交信息,下面是一个范本。


Present-tense summary under 50 characters

* More information about commit (under 72 characters).
* More information about commit (under 72 characters).

http://project.management-system.com/ticket/123

第一行是不超过50个字的提要,然后空一行,罗列出改动原因、主要变动、以及需要注意的问题。最后,提供对应的网址(比如Bug ticket)。

第四步:与主干同步

分支的开发过程中,要经常与主干保持同步。


$ git fetch origin
$ git rebase origin/master

第五步:合并commit

分支开发完成后,很可能有一堆commit,但是合并到主干的时候,往往希望只有一个(或最多两三个)commit,这样不仅清晰,也容易管理。

那么,怎样才能将多个commit合并呢?这就要用到 git rebase 命令。


$ git rebase -i origin/master

git rebase命令的i参数表示互动(interactive),这时git会打开一个互动界面,进行下一步操作。

下面采用Tute Costa的例子,来解释怎么合并commit。


pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

# Rebase 8db7e8b..fa20af3 onto 8db7e8b
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

上面的互动界面,先列出当前分支最新的4个commit(越下面越新)。每个commit前面有一个操作命令,默认是pick,表示该行commit被选中,要进行rebase操作。

4个commit的下面是一大堆注释,列出可以使用的命令。

  • pick:正常选中
  • reword:选中,并且修改提交信息;
  • edit:选中,rebase时会暂停,允许你修改这个commit(参考这里
  • squash:选中,会将当前commit与上一个commit合并
  • fixup:与squash相同,但不会保存当前commit的提交信息
  • exec:执行其他shell命令

上面这6个命令当中,squash和fixup可以用来合并commit。先把需要合并的commit前面的动词,改成squash(或者s)。


pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
s 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

这样一改,执行后,当前分支只会剩下两个commit。第二行和第三行的commit,都会合并到第一行的commit。提交信息会同时包含,这三个commit的提交信息。


# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage

# This is the 2nd commit message:
Fix PostChecker::Post#urls

# This is the 3rd commit message:
Hey kids, stop all the highlighting

如果将第三行的squash命令改成fixup命令。


pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
f 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

运行结果相同,还是会生成两个commit,第二行和第三行的commit,都合并到第一行的commit。但是,新的提交信息里面,第三行commit的提交信息,会被注释掉。


# This is a combination of 3 commits.
# The first commit's message is:
Introduce OpenPGP and teach basic usage

# This is the 2nd commit message:
Fix PostChecker::Post#urls

# This is the 3rd commit message:
# Hey kids, stop all the highlighting

Pony Foo提出另外一种合并commit的简便方法,就是先撤销过去5个commit,然后再建一个新的。


$ git reset HEAD~5
$ git add .
$ git commit -am "Here's the bug fix that closes #28"
$ git push --force

squash和fixup命令,还可以当作命令行参数使用,自动合并commit。


$ git commit --fixup  
$ git rebase -i --autosquash 

这个用法请参考这篇文章,这里就不解释了。

第六步:推送到远程仓库

合并commit后,就可以推送当前分支到远程仓库了。


$ git push --force origin myfeature

git push命令要加上force参数,因为rebase以后,分支历史改变了,跟远程分支不一定兼容,有可能要强行推送(参见这里)。

第七步:发出Pull Request

提交到远程仓库以后,就可以发出 Pull Request 到master分支,然后请求别人进行代码review,确认可以合并到master。

(完)

留言(52条)

谢谢,学习了。

感谢阮老师,git提交的思路终于理清了。

這個感覺跟 git-flow 類似,如果藉由 git-flow 工具可以省事許多 (?)

@张旭:

不一样。git-flow是分支管理,这个是commit管理。

这段执行的结果是不是没说明白?最后不是只应该剩下两个 commit吗?怎么还是3个?

pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
s 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

这样一改,执行后,当前分支只会剩下两个commit。第二行和第三行的commit,都会合并到第一行的commit。提交信息会同时包含,这三个commit的提交信息。

我用过phabricator,它的客户端工具叫arcanist,就是把git简单封装了下,跟这个类似。

输入“arc land”的时候就完成了 squash , rebase, push 的功能。

这种多分支的方式似乎不适应有持续集成习惯的团队。
新功能在分支上开发,就意味着这部分代码在CI上有一段时间不会被测试到,或者是很难被测试到,增加CI配置成本。

兔哥,rebase origin/master 操作和 push --force操作是需要慎重的喔

已经push到远端的代码不应该再rebase,否则就搞乱了提交线了;如果能做到那相应的就不会有需要force的情况了。

以最上面的图来说,就是要注意在做过操作7而未做8之前不应该执行5(pull w/ rebase)操作。

@萧星星:

rebase以后,不用force推不上去啊。如果分支只有一个人开发,我觉得这也不是太大的问题。

如果分支是多人开发,那就应该如你所说,不用rebase了。

git不就是代码快照吗?

好的流程决定好的代码质量

图中第5步应该为fetch 吧?

收藏了,最近正好需要整理这方面的流程。。。~\(≧▽≦)/~

看用什么git 流程吧,这个好像合并分支需要统一管理吧?

合并 commit 了以后不会不好定位问题吗

最后一个是不是merge request

非常感谢您的分享。

这个应该是thoughtbot在github上的git工作流程...

写的很好,学习了!

正确的做法是,本地要有一个分支A和远端的分支保持对应。
然后本地新开分支B开发,提交记录。
如果需要将代码推送到远端的话,就切换到A,首先在A分支上pull同步远端的代码。
然后将分支B上的开发代码merge到分支A上。
然后再往服务端推送。

图解里面的处理,本地就一个分支,既用于和远端的分支映射,又用来做本地的开发,这是不合适的。

终于有一个清晰的使用流程了。
我现在只自己用github,没参与过合作开发,不知道。
以前是做测试的,开发那边经常出逗比的事情,比如pull下来的文件直接用自己的文件覆盖再commit和push(都是master),导致整个项目其他成员的工作全报废。这种事情出过太多次,真心令人崩溃。

引用ChuckLu的发言:

正确的做法是,本地要有一个分支A和远端的分支保持对应。
然后本地新开分支B开发,提交记录。
如果需要将代码推送到远端的话,就切换到A,首先在A分支上pull同步远端的代码。
然后将分支B上的开发代码merge到分支A上。
然后再往服务端推送。

图解里面的处理,本地就一个分支,既用于和远端的分支映射,又用来做本地的开发,这是不合适的。

我新写的一篇文章,http://www.bridgeli.cn/archives/200,和你说的不谋而合

写的很清楚,对于我这非开发人员,也能看清个大概。

阮一峰好似有点误导人,“git push命令要加上force参数,因为rebase以后,分支历史改变了,跟远程分支不一定兼容,有可能要强行推送(参见这里)。”不能随意强推,不然跟你合作开发的会拍死你

还是看廖雪峰的比较正确

赞同ChuckLu的说法
而且force一定要慎用

阮老师,最近在看协同学相关的内容,有点抽象,不太理解,不知道您是否了解,能否写一些关于这方面简单通俗的介绍(之前看过你写的关于熵的介绍,之前的混淆的地方瞬间理解了),谢谢。

为什么git commit后面没有加-m 参数呢,这个不是要提交时的提交备注文本吗?

“提交commit时,必须给出完整扼要的提交信息,下面是一个范本。

Present-tense summary under 50 characters

* More information about commit (under 72 characters).
* More information about commit (under 72 characters).

http://project.management-system.com/ticket/123
第一行是不超过50个字的提要,然后空一行,罗列出改动原因、主要变动、以及需要注意的问题。最后,提供对应的网址(比如Bug ticket)。”

这个意思是罗列具体信息要以 *(星号)作为无序列表?

能不能对autosquash参数,来用命令行自动合并注释举个例子说明。
网上现在没有这样的case。
谢谢~

感叹博主的知识渊博!

很实用

请教个问题, 如果是自己开发项目的情形, 假设不在master上面开发, master只做合并, 测试, 修bug. 下面两种方案哪种好点:

1. 开发用dev分支. 合并dev的时候可以不提交远程直接在本地合并, commit, rebase, merge, 远程上面只保持master分支.
2. 利用github的功能, 总是推到远程, 自己用pull-requset在网页上对比改动进行合并.

我工作中一直使用SmartGit能否介绍使用下

团队里随便rebase会被人拍死的

博主还是加上提醒吧,这文章很容易误导新手

今天仔细研究了一下rebase,发现TortoiseGit只有4个选项:
- pick
- squash
- edit
- skip

比git多一个skip

引用nower的发言:

这段执行的结果是不是没说明白?最后不是只应该剩下两个 commit吗?怎么还是3个?

pick 07c5abd Introduce OpenPGP and teach basic usage
s de9b1eb Fix PostChecker::Post#urls
s 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend

这样一改,执行后,当前分支只会剩下两个commit。第二行和第三行的commit,都会合并到第一行的commit。提交信息会同时包含,这三个commit的提交信息。

3、2合并到1commit,加上4总共两个commit,1、2、3、4的commit message提交信息仍然存在,上面三个提交信息指的是1、2、3、的commit message提交信息

写得很好,支持一下

图中有两个远程主机,而文字解说只有一个主机,图文不对版。我前前后后看过这篇文章和博主另外几篇git相关文章几次,随着git使用经验的积累,今天才看懂文字解说。

与主分支同步,需要使用rebase,这点很重要

阮老师,我前两天在淘宝上买了一本你的es标准入门,现在你的个人网站都成了我的小度娘了。谢谢你!

rebase 还是 merge?
如果纯粹是个人开发,那就完全取决于个人喜好了。
如果涉及团队协作,不建议 rebase。

应该是每个开发者fork一份项目到自己的个人仓库(remote),然后clone个人仓库到本地,将truck仓库设为upstream(名字随意,一般是upstream)。这时候origin应该是指向个人仓库,每次同步代码(fetch + rebase)应该从upstream仓库同步而不是remote仓库,所以第四五里应该是
giti fetch upstream upstream master
git rebase -i upstream/master
不知道理解的对不对

写的不错,坚持更新啊

第三部要怎么做呀!怎么写多行。

第四步:与主干同步
git fetch origin
$ git rebase origin/master

阮老师,第四步有点没看懂啊,
1,这个地方为什么要执行git fetch origin啊?
2,执行git rebase origin/master 之前不需要先checkout 到 master主分支吗?

为何不建议rebase?如果我只rebase自己的多个commit到1个commit,应该没问题吧。这种场景只有rebase可以满足了。

我知道rebase会修改commit号。但在对只有我1个人连续编辑的几个commit之中,使用rebase将它们合起来我看不出来会有风险。

一五年的帖子,这个思路就很牛了,现在还是很牛

学习了,除了force参数慎用以外,其他都挺好的~

学到了,感谢阮老师

文章写的很好,看到有些人评论使用rebase 和 push -f 命令的问题,一般这两个命令通常在自己的需求分支去使用是没有关系的(意思是只有你一个人开发),一般都会在需求分支将提交记录以及修改的都整理好最后再merge分支

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接