团队开发中,遵循一个合理、清晰的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。
(完)
simba 说:
谢谢,学习了。
2015年8月 5日 10:53 | # | 引用
yetson 说:
感谢阮老师,git提交的思路终于理清了。
2015年8月 5日 10:54 | # | 引用
張旭 说:
這個感覺跟 git-flow 類似,如果藉由 git-flow 工具可以省事許多 (?)
2015年8月 5日 11:09 | # | 引用
阮一峰 说:
@张旭:
不一样。git-flow是分支管理,这个是commit管理。
2015年8月 5日 11:19 | # | 引用
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的提交信息。
2015年8月 5日 11:23 | # | 引用
bqdx 说:
我用过phabricator,它的客户端工具叫arcanist,就是把git简单封装了下,跟这个类似。
输入“arc land”的时候就完成了 squash , rebase, push 的功能。
2015年8月 5日 11:24 | # | 引用
崔鹏飞 说:
这种多分支的方式似乎不适应有持续集成习惯的团队。
新功能在分支上开发,就意味着这部分代码在CI上有一段时间不会被测试到,或者是很难被测试到,增加CI配置成本。
2015年8月 5日 12:03 | # | 引用
萧星星 说:
兔哥,rebase origin/master 操作和 push --force操作是需要慎重的喔
已经push到远端的代码不应该再rebase,否则就搞乱了提交线了;如果能做到那相应的就不会有需要force的情况了。
以最上面的图来说,就是要注意在做过操作7而未做8之前不应该执行5(pull w/ rebase)操作。
2015年8月 5日 21:20 | # | 引用
阮一峰 说:
@萧星星:
rebase以后,不用force推不上去啊。如果分支只有一个人开发,我觉得这也不是太大的问题。
如果分支是多人开发,那就应该如你所说,不用rebase了。
2015年8月 5日 22:22 | # | 引用
william 说:
git不就是代码快照吗?
2015年8月 6日 20:08 | # | 引用
terry 说:
好的流程决定好的代码质量
2015年8月 8日 05:52 | # | 引用
戴文源 说:
图中第5步应该为fetch 吧?
2015年8月 8日 09:31 | # | 引用
撒子都学 说:
收藏了,最近正好需要整理这方面的流程。。。~\(≧▽≦)/~
2015年8月10日 15:37 | # | 引用
KevinSu 说:
看用什么git 流程吧,这个好像合并分支需要统一管理吧?
2015年8月12日 14:42 | # | 引用
dopcn 说:
合并 commit 了以后不会不好定位问题吗
2015年8月13日 11:39 | # | 引用
eric.wang 说:
最后一个是不是merge request
2015年8月13日 20:44 | # | 引用
Jian 说:
非常感谢您的分享。
2015年8月14日 13:05 | # | 引用
Liang Qi 说:
这个应该是thoughtbot在github上的git工作流程...
2015年8月17日 20:17 | # | 引用
卢松松博客 说:
写的很好,学习了!
2015年8月17日 21:08 | # | 引用
ChuckLu 说:
正确的做法是,本地要有一个分支A和远端的分支保持对应。
然后本地新开分支B开发,提交记录。
如果需要将代码推送到远端的话,就切换到A,首先在A分支上pull同步远端的代码。
然后将分支B上的开发代码merge到分支A上。
然后再往服务端推送。
图解里面的处理,本地就一个分支,既用于和远端的分支映射,又用来做本地的开发,这是不合适的。
2015年8月18日 11:12 | # | 引用
独行猫儿 说:
终于有一个清晰的使用流程了。
我现在只自己用github,没参与过合作开发,不知道。
以前是做测试的,开发那边经常出逗比的事情,比如pull下来的文件直接用自己的文件覆盖再commit和push(都是master),导致整个项目其他成员的工作全报废。这种事情出过太多次,真心令人崩溃。
2015年8月19日 20:00 | # | 引用
bridgeli 说:
我新写的一篇文章,http://www.bridgeli.cn/archives/200,和你说的不谋而合
2015年8月20日 10:11 | # | 引用
和美视界 说:
写的很清楚,对于我这非开发人员,也能看清个大概。
2015年8月21日 14:07 | # | 引用
sai 说:
阮一峰好似有点误导人,“git push命令要加上force参数,因为rebase以后,分支历史改变了,跟远程分支不一定兼容,有可能要强行推送(参见这里)。”不能随意强推,不然跟你合作开发的会拍死你
2015年8月21日 22:31 | # | 引用
sai 说:
还是看廖雪峰的比较正确
2015年8月21日 22:33 | # | 引用
厉依冰 说:
赞同ChuckLu的说法
而且force一定要慎用
2015年8月24日 17:55 | # | 引用
Rock Chen 说:
阮老师,最近在看协同学相关的内容,有点抽象,不太理解,不知道您是否了解,能否写一些关于这方面简单通俗的介绍(之前看过你写的关于熵的介绍,之前的混淆的地方瞬间理解了),谢谢。
2015年8月24日 19:12 | # | 引用
Zz 说:
为什么git commit后面没有加-m 参数呢,这个不是要提交时的提交备注文本吗?
2015年8月27日 21:36 | # | 引用
CodingNinja 说:
“提交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)。”
这个意思是罗列具体信息要以 *(星号)作为无序列表?
2015年9月 1日 00:33 | # | 引用
michael 说:
能不能对autosquash参数,来用命令行自动合并注释举个例子说明。
网上现在没有这样的case。
谢谢~
2015年9月 4日 09:47 | # | 引用
雷雨 说:
感叹博主的知识渊博!
2015年9月 6日 17:27 | # | 引用
carl 说:
很实用
2015年9月25日 11:51 | # | 引用
lhrkkk 说:
请教个问题, 如果是自己开发项目的情形, 假设不在master上面开发, master只做合并, 测试, 修bug. 下面两种方案哪种好点:
1. 开发用dev分支. 合并dev的时候可以不提交远程直接在本地合并, commit, rebase, merge, 远程上面只保持master分支.
2. 利用github的功能, 总是推到远程, 自己用pull-requset在网页上对比改动进行合并.
2015年10月 3日 22:32 | # | 引用
刘凤杰 说:
我工作中一直使用SmartGit能否介绍使用下
2015年10月30日 10:20 | # | 引用
随便rebase害死人 说:
团队里随便rebase会被人拍死的
博主还是加上提醒吧,这文章很容易误导新手
2015年10月31日 02:45 | # | 引用
石樱灯笼 说:
今天仔细研究了一下rebase,发现TortoiseGit只有4个选项:
- pick
- squash
- edit
- skip
比git多一个skip
2015年12月 9日 11:01 | # | 引用
鹓雏 说:
3、2合并到1commit,加上4总共两个commit,1、2、3、4的commit message提交信息仍然存在,上面三个提交信息指的是1、2、3、的commit message提交信息
2016年2月10日 05:58 | # | 引用
小伍 说:
写得很好,支持一下
2016年5月13日 23:48 | # | 引用
listen 说:
图中有两个远程主机,而文字解说只有一个主机,图文不对版。我前前后后看过这篇文章和博主另外几篇git相关文章几次,随着git使用经验的积累,今天才看懂文字解说。
2017年1月21日 22:30 | # | 引用
Clay Chen 说:
与主分支同步,需要使用rebase,这点很重要
2017年2月27日 21:45 | # | 引用
何旭林 说:
阮老师,我前两天在淘宝上买了一本你的es标准入门,现在你的个人网站都成了我的小度娘了。谢谢你!
2017年4月 5日 19:26 | # | 引用
scPmYGit 说:
rebase 还是 merge?
如果纯粹是个人开发,那就完全取决于个人喜好了。
如果涉及团队协作,不建议 rebase。
2017年4月28日 18:56 | # | 引用
robin 说:
应该是每个开发者fork一份项目到自己的个人仓库(remote),然后clone个人仓库到本地,将truck仓库设为upstream(名字随意,一般是upstream)。这时候origin应该是指向个人仓库,每次同步代码(fetch + rebase)应该从upstream仓库同步而不是remote仓库,所以第四五里应该是
giti fetch upstream upstream master
git rebase -i upstream/master
不知道理解的对不对
2017年8月 6日 00:40 | # | 引用
毕向东 说:
写的不错,坚持更新啊
2017年10月10日 00:24 | # | 引用
jiajiayu 说:
第三部要怎么做呀!怎么写多行。
2017年10月18日 16:41 | # | 引用
giants 说:
第四步:与主干同步
git fetch origin
$ git rebase origin/master
阮老师,第四步有点没看懂啊,
1,这个地方为什么要执行git fetch origin啊?
2,执行git rebase origin/master 之前不需要先checkout 到 master主分支吗?
2017年11月28日 17:09 | # | 引用
rebase探讨 说:
为何不建议rebase?如果我只rebase自己的多个commit到1个commit,应该没问题吧。这种场景只有rebase可以满足了。
2018年2月12日 11:25 | # | 引用
rebase探讨 说:
我知道rebase会修改commit号。但在对只有我1个人连续编辑的几个commit之中,使用rebase将它们合起来我看不出来会有风险。
2018年2月12日 11:31 | # | 引用
醉舞经阁半卷书 说:
一五年的帖子,这个思路就很牛了,现在还是很牛
2021年7月 1日 14:58 | # | 引用
w 说:
学习了,除了force参数慎用以外,其他都挺好的~
2021年10月21日 08:57 | # | 引用
阿油 说:
学到了,感谢阮老师
2022年1月23日 19:38 | # | 引用
summer 说:
文章写的很好,看到有些人评论使用rebase 和 push -f 命令的问题,一般这两个命令通常在自己的需求分支去使用是没有关系的(意思是只有你一个人开发),一般都会在需求分支将提交记录以及修改的都整理好最后再merge分支
2022年3月 7日 11:37 | # | 引用