Git workflow 详谈
zphuang
8年前
<p>作为一名工程师, Git 在日常开发中是不可或缺的工具。</p> <p>这里详细介绍几种比较常用的基于 Git 的工作流模型, 以便于团队协作的规范化和效率提升。</p> <h2>中心化工作流</h2> <p>使用过SVN的应该都知道, SVN使用的是集中式管理流程, 如果你刚从SVN 切换到 Git , 你可以尝试使用中心化工作流的方式。这样,你几乎不需要变更之前的工作方式, 就可以完成平滑的过渡了。 而且在使用过程中还可以看到 Git 优于 SVN 的地方:</p> <p>第一,每个成员都可以在本地拥有一份完整的项目代码仓库,而不只是一个工作区的副本,任何人都可以在本地执行 add 和 commit ,而不需要考虑远端仓库是否有变更,直到需要的时候再去提交即可。</p> <p>第二,Git 的工作区、暂存区、引用更新等设计,可以给开发者更多自由来切换当前工作,且不会造成代码丢失。</p> <h2>工作细节</h2> <p>中心化工作流的方式是:在远端(远端可以是服务器端,也可以是本地的任意目录)新建一个仓库,默认是 master 分支,作为唯一的中心仓库。 所有人都 clone 这个仓库作为本地仓库,并在本地仓库进行开发。本地的提交是和远端仓库无关的,等需要的时候再 push 进主仓库的 master 分支即可。</p> <p>在这种方式下, 远端是唯一确定的中心仓库, 所有人都要以这个仓库为准。 所以,在提交之前要先 fetch 最新提交,在这些提交之上作出自己的更改(一般我们使用 rebase 来完成)。</p> <p>如果本地的修改和远端仓库中的变更发生了冲突,那么 Git 会暂停 rebase ,并让你来解决这些冲突。我们可以很简单的使用 git status 和 git add 等命令完成冲突的合并。 另外, 如果我们解决不了冲突, 我们也可以使用 git rebase --abort 很容易的退出 rebase 的过程。</p> <p>这样每天的工作方式就变成了,从中心仓库拉取最新代码, 然后开始一天的工作, 开发完成后,拉取中心仓库的更新, 合并代码后, 再提交至中心仓库, 结束一天的工作。 这样的好处就是不需要变更原先(使用SVN)的工作方式。当然弊端也很明显,你并不知道中心仓库的代码是否是稳定的,或者说并不能确定当你的代码和中心仓库代码合并后,是否是稳定的,带来的问题就是开发进度和回滚不那么方便控制。</p> <h2>示例</h2> <p>我们有两位程序员, A 和 B, 两人同时在对一个项目做开发, 并且使用 Git 的中心化工作流方式。</p> <p>1.创建远端中心仓库</p> <p>这里我们有两种方式:</p> <ul> <li> <p>借助于已经搭建好的平台 GitHub/GitLab 之类的,点击 create repo 即可。</p> </li> <li> <p>在远端(这里只是为了区别本地仓库,事实上,使用任何一个其他人可以连通的机器都可以,包括自己本地其他目录) 创建一个 <strong>裸仓库</strong> ,创建裸仓库和我们平时创建本地仓库的区别,可以参考我另一篇文章Git 本地仓库和裸仓库 。</p> </li> </ul> <p>这里以第二种方式为例:</p> <pre> # --bare 参数必须有 git init --bare /the/repo/path.git</pre> <p>2.所有人都 clone 中心仓库到本地作为本地仓库</p> <pre> git clone /the/repo/path.git</pre> <p>注意仓库地址必须是正确的, 且有权限访问才能 clone 成功。</p> <p>3.程序员 A 在他的本地仓库进行功能开发并进行发布</p> <p>一般情况下,我们通过 git status 看看当前状态,并通过 git add 和 git commit 等命令完成本地仓库的提交。 当然这个提交影响的也只是本地仓库而已,并没有对中心仓库产生任何影响,所以我们既不需要关心别人有什么提交,也不用担心我们当前的提交是否对别人造成了影响。当 A 认为自己所开发的功能已经完成, 那他将执行 git push origin master 这样的操作,将自己本地仓库所有不存在于中心仓库的提交都 push 到远端的中心仓库上。</p> <p>4.程序员 B 在他本地仓库进行功能开发</p> <p>B 在 clone 中心仓库后所做的操作和 A 一样,在本地仓库进行项目开发,并在本地仓库进行提交,他不需要知道中心仓库发生了什么样的变化。</p> <p>5.程序员 B 将自己开发的功能并进行发布</p> <p>B 在确认自己开发的功能已经完成后,想要将自己的代码通过 git push origin master 这样的操作发布至中心仓库,但是却被中心仓库提示他的修改已经和中心仓库有了分叉, 需要他先执行 git pull 之类的操作, 将中心仓库上 A 的提交与 B 本地的提交进行合并才允许他并入中心仓库。所以,他执行了 git pull --rebase origin master 来将中心仓库的修改并入他的本地仓库。使用 --rebase 参数的意义在于 fetch 执行完成后,将把 B 的所有提交移至 master 顶端。</p> <p>当然这里不使用 --rebase 参数也会成功,只不过是会生成一个合并提交,有些情况下使用 --ff 参数也可以避免产生合并提交。在这里使用 --rebase 只是一个建议操作。</p> <p>如果 A 和 B 修改的文件没有关联,一般情况下会直接完成合并,如果发生冲突,Git 将会暂停 rebase 的过程,并列出当前冲突的文件,你可以简单的使用 git status 和 git add 等命令进行合并,合并后使用 git rebase --continue 继续 rebase 的过程。或者使用 git rebase --abort 退出 rebase 过程。</p> <p>在 B 合并完成后,可以执行 git push origin master 将自己开发的功能发布至中心仓库。</p> <p>至此,基础的中心化工作流方式就介绍完了,但是这里也很容易看出来其中的问题,除了前面说到过的以外,还有就是效率低下,如果很多人都在持续进行提交,那很影响新功能的提交(多人持续性进行提交)。 一个比较容易提升效率的方式就是切换到 <strong>特性分支工作流</strong> 的方式。</p> <h2>特性分支工作流</h2> <p>基于特性的分支工作流,可以为每个特性做隔离,避免对中心仓库主干代码造成影响。</p> <h2>工作细节</h2> <p>顾名思义, 就是根据每个特性都会开一个新的分支,每个分支都应该包含着描述性的名称,无论是一个人开发,还是多人协同,该特性的全部开发工作都在这个分支上进行。待该特性开发完成后, 并入主分支,然后删除分支,代码上线。</p> <p>这种情况下, 最大的优势在于, 所有的特性开发都可以并行处理。 不必要像中心化工作流方式, 每个人的变动都可能引起其他的人的代码合并, 并且所有功能都杂糅在一起, 从测试和回滚都会变得很繁琐。 另外的一个好处就是特性分支可以推送到中心仓库,这样也便于单独测试。</p> <p>这里需要注意的是,特性分支往主分支合并的时机,应该是该特性开发完成,并测试通过,避免对主干代码造成污染。</p> <p>在进行分支隔离后,我们发现,我们当前只处理了开发模式,但并没有涵盖一个很完备的产品生命周期, 开发、发布、维护等过程,所以,我们有了 Gitflow 工作流。</p> <h2>Gitflow 工作流</h2> <p>基于Gitflow 的工作流方式, 这种工作流方式, 主要是管理着新功能开发,发布及维护等模式,根据不同类型的工作对分支进行定义, 分为 <strong>特性分支</strong> , <strong>修复分支</strong> , <strong>release 分支</strong> , <strong>开发分支</strong> 和 <strong>主分支</strong> 。</p> <p>主分支:中心仓库建立后的默认 master 分支(当然使用其他分支也可以,但要保证该分支是受保护的)。主分支随时保持代码是稳定的,并且有明确的版本标签,后续代码回滚等操作都将从主分支进行。</p> <p>开发分支:中心仓库建立后,从 master 分支切出来,此时与 master 分支保持一致。后续演进中,开发分支随时保持代码最新,但却不一定是线上实际运行的代码。</p> <pre> git checkout -b develop</pre> <p>特性分支:应该从开发分支切出,开发完成后, 再合并进入开发分支, 如果达到了发布标准, 则从开发分支切出 release 分支, 切出来的这个分支,只做该版本内的代码修复, 不再加入新功能, 这时此分支处于锁定的状态。</p> <p>修复分支, 用于对线上主分支代码的及时修复, 待修复完成后, 合并进入主分支, 再并入开发分支。 修复分支只能从主分支切出。</p> <p>发版分支, 一般命名为 release-xxx 这个分支只能从开发分支切出, 最后并入主分支,打上版本号的标签,它也应该并入开发分支,如果中间有其他修复的话。</p> <h2>fork 工作流</h2> <p>fork 分支流和上面介绍的所有工作流都不太一样。它的上游有一个唯一仓库, 所有人都是 fork 这个仓库, 在自己的远端和自己的本地各维护一个仓库,待开发完成后推入自己的远端仓库,并结合 GitHub/GitLab等提交 Pull Request,进入了 review 阶段,待通过后,将会被合并入上游唯一的仓库。这种方式比较适合 GitHub 中的大型开源项目, 对于小团队的内部项目, 这种方式可能未必合适。</p> <p>而且 fork 工作流, 会占用更多的资源(毕竟每个人都维护一份远端仓库)。 而且每个人都看不到其他人的动态,只有当提交 Pull Request 的时候, 才知道每个人发生了什么。</p> <h2>总结</h2> <p>我个人比较推荐的是 Gitflow 的开发工作流, 这种方式下,一切都是可控的, 每个分支都有各自独立的功能,目的性很明确, 同时,在做代码回滚之类的操作也是可以直接剔除。 另外, 在这种工作流方式下, 团队中的每个人都能很轻易的知道其他人在做什么, 做出了什么样的改变, 对于团队协作, 或许更加合适。</p> <p>当然所有的工作流并不一定能完全套用, 可以吸取一些规范, 合并入自己的日常工作, 将代码仓库的合作流程标准化和规范化, 这也是一切自动化的基础。</p> <p> </p> <p> </p> <p>来自:https://segmentfault.com/a/1190000007692929</p> <p> </p>