Git 工做流

本文是我使用 Git 一段时间和看过一些资料后的总结,以及我的看法,深感 Git 的规范使用很是重要,不规范的使用会带来不少麻烦。html

同类工具比较

SVN与Git

原理上

  1. Git直接记录文件快照,SVN每次记录哪些文件做了更新、更新哪些行的内容
  2. Git 有本地仓库和远程仓库,SVN没有本地仓库
  3. Git 大多数操做是本地执行,SVN操做几乎都须要链接网络

操做上

  1. Git提交后保存在本地仓库,须要推到远程仓库;SVN提交后在远程仓库
  2. Git有各类“反悔”命令,SVN几乎没有
  3. Git有真正的branch,svn的branch实际上是工做空间的副本

Git的使用现状

  • 意义不明的提交信息

image

  • 糟糕的版本树图

image

存在的问题

  • 合并解决冲突容易出错
  • 回滚时难以经过提交信息寻找回滚版本号
  • 合并点可能看不出这次合并影响的文件

Git 工做流

Gitflow 工做流

Gitflow 为不一样的分支分配一个很明确的角色,并定义分支之间如何和何时进行交互。分别有历史分支、功能分支、发布分支和维护分支。git

历史分支

使用两个分支来记录项目的历史。 master分支记录了正式发布的历史,而develop分支做为功能的集成分支。所以,master分支的每次提交都应分配一个版本号。github

功能分支

功能分支是从developcheckout出来的新分支,每一个功能对应一个分支。shell

1.假设开发a功能:bash

git checkout -b feature-a develop
复制代码

2.当新功能完成时,合并回develop分支。网络

git checkout develop
git merge --no-ff feature-a
git push
git branch -d feature-a
复制代码

发布分支

1.当develop分支开发到须要发布时,从develop分支拉出一个发布分支,命名为release-*release/*ide

git checkout -b release-0.1 develop
复制代码

2.该分支用于发布循环,只作bug修复、文档生成等面向发布的任务。新功能再也不添加到这个分支上。 3.一旦发布完成,把发布分支mergemaster分支上。svn

git checkout master
git merge --no-ff release-0.1
git push
复制代码

4.打tag记录版本号,方便跟踪每次发布。工具

git tag -a 0.1 -m "release 0.1 publish" master
git push --tags
复制代码

5.把这些重新建发布分支以来作的修改mergedevelop分支。测试

git checkout develop
git merge --no-ff release-0.1
git push 
复制代码

6.最后删除发布分支

git branch -d release-0.1
复制代码

维护分支/热修复

当线上版本出现bug时,就须要用到维护分支,它用于快速给产品发布版本打补丁。

1.从master分支拉一个维护分支(这是惟一从master分支拉出来的分支)。

git checkout -b hotfix master
复制代码

2.修复完成后,立刻合并回masterdevelop

git checkout master
git merge --no-ff hotfix
git push
git checkout develop
git merge --no-ff hotfix
git push 
git branch -d hotfix
复制代码

3.master用新版本号打tag

git tag -a 0.2 -m "release 0.2 publish" master
git push --tags
复制代码

图解

image
图片来自网络

优势

  • 单个功能独立开发,并行开发互不干扰
  • masterdevelop分支分别记录发布和功能开发的历史
  • 因为有发布分支,其余暂不发布的功能的开发不受发布的影响,能够继续提交
  • 维护分支能快速打补丁,不影响正在开发的功能

缺点

  • 复杂,分支繁多
  • Git GUI不支持,纯命令行
  • 对开发者要求高(理解工做流,熟悉Git命令)
  • 全部功能分支基于不稳定的develop
  • 须要维护两个长期分支masterdevelop

GitHub Flow

GitHub 使用的工做流

  1. 全部在master上的东西都是可发布的(已发布或立刻发布)
  2. 开发新功能时,从master拉一个名称清晰的新分支
  3. 在本地提交到这个分支的同时把它push到远程仓库
  4. 当你须要获得反馈或帮助,或者该分支准备merge时,打开一个pull request
  5. 该分支被review且赞成合并后,合并到master
  6. pushmaster后,应该当即发布

优势

  • 操做简单
  • 主干的代码有质量保证

缺点

  • 测试线和正式环境的发布没有区分

Git-Develop

develop做为固定的持续集成和发布分支

  1. 每个功能都从master拉一个功能分支。
  2. 在这个功能分支上开发,功能完成到发布时,提交code review,经过后自动合并到develop
  3. 待全部计划发布的变动分支代码都合并到develop后,rebase masterdevelop,完成发布。
  4. 应用发布成功后打一个tag
  5. develop分支的发布版本合并回master

优势

  • 操做相对简单
  • 流程稍做改动,便可区分测试线和正式环境
  • 每次开发都基于正式版本最新的代码(master),和当前开发的其余分支不产生依赖关系
  • master始终是已发布状态

缺点

暂时想不到。。。

Pull Requests

Pull requests不是一种工做流,而是一个能让开发者更方便地进行协做的功能,能够在提议的修改合并到正式项目以前对修改进行讨论。这种方式对分支的合并有一些限制,例如只有项目维护者有权限合并分支到仓库中。其工做方式:

  1. 开发者在本地仓库新建一个功能分支。
  2. 功能完成后,开发者push分支修改到远程仓库中。
  3. 开发者发起Pull requests
  4. 团队成员收到通知,进行code review,讨论和修改。
  5. 项目维护者合并功能到仓库中并关闭Pull Requests

Git使用技巧

git commit --amend

git commit --amend最多见的用法是上次提交信息写错,或提交文件多了或漏了之时,从新提交覆盖上一次提交。

其实它还有一个用法,就是用来合并提交。例如上次提交的修改并不彻底,再做修改以后能够用该命令把本次提交与上次提交合并在一块儿。

git reset

在须要回滚一次或屡次提交时,能够用git reset。因为该命令比较危险,建议用于已经把最新提交推到远程仓库上的本地分支。

  • git reset HEAD [filename]

把已在暂时区的文件取消,恢复到已修改未暂存状态。

  • git reset HEAD~[n]

git reset后面可带参数HEAD~[n](n >= 1)。表示回退到n个提交以前。同时,它也能够用来合并提交。下面的写法与git commit --amend结果是同样的。

git reset HEAD~1
git commit
复制代码

下面的用法则是合并了屡次提交

git reset HEAD~2
git commit
复制代码
  • git reset [version]

git reset后面也能够带版本号,直接回退到指定版本。

  • git reset的三种参数

    1. 使用参数--hard,如git reset --hard [version]会执行如下操做:
      1. 替换引用的指向。引用指向新的提交ID。
      2. 替换暂存区。替换后,暂存区的内容和引用指向的目录树一致。
      3. 替换工做区。替换后,工做区的内容变得和暂存区一致,也和HEAD所指向的目录树内容相同。
    2. 使用参数--soft,如 git reset --soft [version]会执行上述的操做a。即只更改引用的指向,不改变暂存区和工做区。
    3. 使用参数--mixed或者不使用参数(默认为--mixed),如 git reset [version]会执行上述的操做a和b。即更改引用的指向及重置暂存区,可是不改变工做区。

git merge --no-ff

--no-ff是不快速合并的意思

与git merge的区别

git merge的结果:

被merge的分支和当前分支在图形上并为一条线,被merge的提交点逐一合并到当前分支。

image

git merge --no-ff的结果:

被merge的分支和当前分支不在一条线上,被merge的提交点还在原来的分支上,同时在当前分支上产生一个合并点。

image

git rebase

git rebase通常解释为变基,也有解释为衍合,我的以为变基比较容易理解。

与git merge的区别

git merge是把两个分支的代码合并到一块儿,其实git rebase也是相同的做用,可是表现上是两种不一样的形式。

例如如今 dev 提交了一次,master 在此以后也提交了一次,两个分支的状态以下:

image

  • 提交点顺序

能够看出git merge后,不管加不加--no-ff参数,提交点的顺序都和提交的时间顺序相同,即 master 的提交在 dev 以后,如图:

image

git rebase后,顺序就变成被rebase的分支(master)全部提交都在前面,进行rebase的分支(dev)提交都在被rebase的分支以后,在同一分支上的提交点仍按时间顺序排列,如图:

image

  • 变基

从上面的图能够看出,dev 在rebase master 后,分支发生了变化,本来是两个分支,rebase的结果看起来是: dev 是基于 master 的分支,且产生了一些新提交。

通常来讲,rebase后的 dev 和远程的origin/dev会发生分离,在命令行界面中会提示:

Your branch and 'origin/dev' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
复制代码

这时须要用git push -f强制推送,覆盖远程分支。若使用了提示中的git pull,结果会变成合并,并产生一个合并提交点。

注:慎用git push -f!

git pull --rebase

注意git pull时请加上--rebase,理由下面会说。

与git pull的区别

在通常状况下,加与不加--rebase是没有区别的。然而,结合上面说的git rebase功能,能够知道某个分支可能与其远程分支发生分离,而当你pull时,你的本地分支仍是和原来的远程分支同样,这时若是使用git pull,则会变成你的本地分支和远程分支合并,正确的作法是git pull --rebase,才会拉取到最新的分支。

因此推荐在任什么时候候pull远程分支,最好加上--rebase参数。

git reflog

查看提交记录的命令是git log,而git reflog的功能是查看本地操做记录,如此一来能够看到本地的commit, merge, rebase等操做记录。

6fe46ab HEAD@{0}: rebase finished: returning to refs/heads/dev
6fe46ab HEAD@{1}: rebase: dev modify a
2c92bcb HEAD@{2}: rebase: checkout master
9b26f5d HEAD@{3}: reset: moving to 9b26f5db1e8597b884c45114fbbff36c440da274
5531fc0 HEAD@{4}: merge master: Merge made by the 'recursive' strategy.
9b26f5d HEAD@{5}: checkout: moving from master to dev
复制代码

Git工具推荐

  • SourceTree
  • GUI Clients

参考资料

Pro Git,Git工具书: http://iissnan.com/progit/html/zh/ch1_5.html

Git 工做流指南,介绍了几种主流工做流: https://github.com/xirong/my-git/blob/master/git-workflow-tutorial.md

Gitflow 有害论: http://insights.thoughtworkers.org/gitflow-consider-harmful/

GitHub Flow: http://scottchacon.com/2011/08/31/github-flow.html

Google 的“主干开发”(trunk-based development): http://www.ruanyifeng.com/blog/2016/07/google-monolithic-source-repository.html

相关文章
相关标签/搜索