• 【Beta阶段】团队源代码管理


    0. 快速上手与理解

    • 如果你的团队来了一个新队员,有一台全新的机器,你们是否有一个文档,只要设置了相应的权限,她就可以根据文档,从头开始搭建环境,并成功地把最新、最稳定版本的软件编译出来,并运行必要的单元测试?(在这过程中,不需要和老队员做任何交流)

    其实我们非常想说可以,但是最后在仔细考虑后发现确实是不可以的。我们的文档注重的方面更多地在理解用户需求与后端的设计上,但是关于配置环境,运行单元测试这方面的文档,虽然后端工程师在启程时写过一篇,如下:

    但是后期并没有对它进行必要的完善与维护,并且没有将它push到github上,所以我们即使想说是,但是确实是不可以的。我们没有重视对于环境与运行方面的文档。

    1. 你的团队的源代码控制在哪里?用的是什么系统?如何处理文件的锁定问题?

    场景: 程序员果冻正在对几个文件进行修改,实现一个大的功能, 这时候,程序员小飞也要改其中一个文件,快速修复一个问题。怎么办?

    一个代码文件被签出 (check out) 之后,另一个团队成员可以签出这个文件,并修改,然后签入么?

    有几种设计,各有什么优缺点?

    例如,签出文件后,此文件就加锁,别人无法签出;  或者, 所有人都可以自由签出文件

     

    答:我们团队的源代码控制在Github上,使用的Git系统与Github本身集成的一些其他功能。

    由于Git的特点,在处理文件的合并这方面有着易懂且优秀的合并技巧。

    以下是场景分析:

    程序员果冻现在正在对几个文件进行修改,程序员小飞要修改其中一个文件,修复一个大bug。果冻因为还没有修改完,还没有push。但是很快,小飞修改完成了,commit,push成功!小飞乐滋滋地想着自己即将到来的奖金,去吃饭了。办公室就剩下果冻一个人在紧张赶工….也是轻松且愉悦地commit,push。

    啊?被拒绝了!

    Pushing to git@github.com:buaase/Phylab-Web.git

    To git@github.com:buaase/Phylab-Web.git

    ! [rejected] master -> master (non-fast-forward)

    果冻心里很是郁闷,难道就因为我长得没小飞帅,就要拒绝我的提交吗?

    嘿嘿,其实不是的,其实这是因为果冻在进行push之前,小飞已经推送了内容到远程数据库,果冻的push就被拒绝。因为直接push会覆盖小飞所修改的内容。

    所以作为项目经理的我,在跟我们团队的队员强调Git的使用时反复强调一点,在所有的push之前一定要进行pull,将服务器端他人推送的内容先合并到本地。

    比如我们在项目中演示如下:

    关于文件的锁定这一点上,git本身的合并机制支持了多个人同时开发不同的feature,不论是通过同一个分支,或者是不同的分支。如果在同样的时间内,一个文件只能被一个人修改的话,虽然是不会产生冲突,但是在某些项目里,这样类串行化的作业方式效率较低。当然在实际使用git的过程中应该尽量避免冲突,能够自动merge就自动合并。

     

    2. 如何看到这个文件和之前版本的差异? 如何看到代码修改和工作项 (work item),缺陷修复 (bug fix) 的关系。

       场景: 程序员果冻看到某个文件被修改了,他怎么看到这个文件在最近的修改究竟改了哪些地方? 

       场景: 程序员果冻看到某个文件在最新版本被改动了100 多行, 那么和这100多行对应的其他修改在什么文件中呢? 这个修改是为了解决哪些问题而作的呢? 那些问题有工作项 (work item,issue),或者bug 来跟踪么?

    答: 在git管理中, 使用git diff即可看到文件和之前版本的差异。

    • git diff:是查看working tree(工作目录)与index file(暂存区)的差别的。

    • git diff --cached:是查看index file(暂存区)与commit(本地仓库)的差别的。

    • git diff HEAD:是查看working tree(工作目录)和commit(本地仓库)的差别的。

    在git里,这三个概念是很重要的,其中 git add的一个功能是将修改从工作目录添加到index file,commit的工能就是从index file的修改添加到 commit。

    比如下面这是一个截图:(使用git diff --cached)

    当我们把a.txt文件提交并作出修改后,可以使用git diff来查看差异:

    在Github中,也可以通过可视化的界面来看到每次修改的代码(包括增加,删除)文件与修改的具体位置,修改的具体内容。

    比如下面这样的界面:

    可以看到每次修改的文件路径、文件的内容、红色代表删除掉的内容,绿色代表添加的内容

    修改为了解决的问题要通过commit日志的提交信息来实现,但是很遗憾,在我们的团队项目中,关闭Issue和commit日志信息都做得不太好。大家的commit日志除了黄雨萌和我的相对规范一些,队员们很多都用来当打油诗了…

    一次commit可以和Issue相关联,以表明这个Issue是由本次commit修复的,只需要在git commit的时候使用时加入如下commit日志信息即可:

    • fix #xxx

    • fixes #xxx

    • fixed #xxx

    • close #xxx

    • closes #xxx

    • closed #xxx

     比如我使用了如下语句:

    现在可以在github看到

    可以看到最后那里,一次commit和issue的关闭是绑定的。

    3. 如果某个文件在你签出之后已经被别人修改,并且签入了,那么你在签入你的修改的时候, 如何合并不同的修改(merge)? 你用了什么工具来帮助你?

    答: 我们使用Git来帮助我们完成了这件事。

    在一般情况下,git pull后git会自动合并Git修改的部分,自动的Merge。但是,也存在无法自动合并的情况。就像果冻和小飞这样,远程数据库和本地数据库的同一个地方都发生了修改的情况下,因为Git无法自动判断要选用哪一个修改,所以就会发生冲突。但是,Git会在发生冲突的地方打个标记!比如这样式的:

    <<<<<<< HEAD

    test in Local

    =======

    test in Remote

    >>>>>>> 17c805…(Commit的Hash值)

    • ==分割线上方是本地数据库的内容

    • ==分割线下方是远程数据库的某次产生冲突的commit所修改的内容。

    这时候我们需要通过一双慧眼来识别哪些都可以保留,哪些保留远程数据库的内容,哪些保留本地数据库的内容。在将文件冲突的内容合并后,删除掉<<<<< 和=====,>>>>>这样的东西,重新add,commit,push,即完成了一次手工合并。

    不过因为大家都是新手入门,不太会合理地人工合并冲突,所以项目经理本身在分配任务时,遵循了一个原则:尽量不让两个人的任务在同一个文件上产生重叠

    每个人修改的文件范围或者其他都是固定的,一般不会存在两个人同时修改同样的文件。当然,前端和后端在修改时大部分时候都会产生冲突,这时候我们就使用了另一套机制来帮我们实现这一点:

    新建分支与分支合并。

    Beta阶段,我们要用到一个论坛的框架与现有的一些逻辑,但是又要同时兼容之前的网站主页的风格与设计,为了实现这一点,我们的前端负责修改页面样式,后端负责对接后台逻辑与数据库的同步问题。那么问题来了,如果两个人在同一个分支下进行工作,那么每次都要有合并与修改记录,并且如果一个人修改的部分因为某些bug不可用的时候,另一部分也无法确认其所修改的部分是否正确。所以我们建立了两个分支(Back-end和Front-end),Front-end上只有一个wecenter的模版供前端进行样式的修改,而后端使用默认的wecenter的模版进行数据库的对接与其他工作,两份工作由此而开始独立进行,互不干扰。

    我们团队项目里的Front-end分支和Back-end分支的情况如下:

    等到前端做到可行的程度后,前后端修改的部分开始合并,合并两个分支来将前端修改的部分合并到Back-end分支上,这样合并的工作只在一天进行,且由于冲突的部分较少,大部分都是自动合并,效率很高。

     

    4. 你有20个文件都是关于同一个功能的修改,你要如何保证这些文件都同时签入成功(修改的原子性),或者同时签入不成功?

         

    场景: 程序员果冻要签入 20 个文件,他一个一个地签入, 在签入完5 个 .h 文件之后, 他发现一些 .cpp 文件和最新的版本有冲突,他正在花时间琢磨如何合并... 这时候, 程序员小飞从客户端同步了所有最新代码, 开始编译, 但是编译不成功 - 因为有不同步的 .h 文件和 .cpp 文件!  这时候, 别的程序员也来抱怨同样的问题,果冻应该怎么办?

    答:果冻目前面临的问题恰恰可以由我们第三点所述的branch的好处解决。

    Branch的出现,可以让任何一位开发者基于其他人的代码或环境都完整可用(即stable版)的环境下进行自己的部分的独立开发 。

    最后的合并工作可以放在一天之内,将所有的Branch上的feature合并到一个dev分支上来。但是这样面临的风险也是有的,多个分支同时合并时如果出现了比较大的冲突,合并起来必须小心翼翼。

    同时,在解决一个Issue的时候,也可以新建一个Issue分支,比如像如下所示:

    在解决了Issue后,可以使用分支合并的技术将两个或多个分支合并。

    5. 你的PC 上有关于三个功能的修改,但是都没有完成,有很多文件处于半完工的状态,这时你要紧急修改一个新的 bug,如何把本地修改放一边,保证在干净的环境中修改这个 bug, 并成功地签入你的修改 --- changelist management

       

     

    答:在Git里,不能完整地保证commit后整个环境处于可编译或可运行状态下的commit是不好的提交。

    所以在文件半完工的状态下,我们不可以使用commit来将文件修改的内容留下来。

    Git为我们提供了一种类似于操作系统里的保存现场的指令,那就是stash。 它可以把当前工作现场"储藏"起来,等以后恢复现场后继续工作,使用方法类似下面:

    $ git stash

    Saved working directory and index state WIP on master: 5655bdc Merge branch 'mas

    ter' of https://github.com/buaase/Phylab-Web

    HEAD is now at 5655bdc Merge branch 'master' of https://github.com/buaase/Phylab

    -Web

     

    这时候就会发现,add了以后的东西都被"雪藏"起来,现在的工作区非常干净,我们这时候可以在一个干净的环境中修复紧急的bug并提交,签入,在push后,再使用

    git stash apply 或者 git stash pop

    来将保存起来的内容取出来继续开开心心地开发啦。

    比如下面我们来实际操作演示一下

     

    可以看到我们对a.txt进行了修改,下面我们使用stash指令来让它”保存现场“

    可以看到实际上 git stash后,我们对于a.txt的修改不见了?!

    再使用 git stash pop,再来一观:

    可以看到 git stash pop 后,对a.txt的修改又回来了。

    6. 如何给你的源代码建立分支

        场景:你们需要做一个演示,所以在演示版本的分支中对各处的代码做了一个临时的修改,同时,主要的分支还保持原来的计划开发。你们怎么做到的?在演示之后,演示版本的有些修改应该合并到主分支中,有些则不用,你们是怎么做到的?

        场景:你们的软件发布了,有很多用户,一天,一个用户报告了一个问题,但是他们是用某个老版本,而且没有条件更新到最新版本。这时候,你如何在本地构建一个老版本的软件,并试图重现那个问题?

       

    给源代码建立分支的过程我们下面使用一个gif图来演示一下:

    上面这个gif图演示了我们使用cherry pick的一个完整流程(第一次录制gif见谅)

    主要流程是我们建立了两个分支 dev1和devtest分支,并使用cherry-pick将dev1上的commit(日志信息为"test")合并到devtest分支上来,并且最后造成devtest分支也有该commit提交。

    并且在这过程中通过命令行建立了远程分支。

    当用户没有条件更新到新版本的时候,我们将新建一个分支,然后使用git reset hashcode(commit日志的唯一hash码,可以倒退回过去),然后进行测试以重现问题。

    全过程如下所示:

    我们可以看到,我们从fixed  #189的HEAD 回退到了 b5320c所代表的commit记录处。

    7. 一个源文件,如何知道它的每一行都是什么时候签入的,为了什么目的签入的 (解决了哪个任务,或者哪个bug)?

       场景:一个重要的软件忽然出现崩溃的情况, 程序员果冻经过各种debug手段,发现问题是在某一个文件中有一行代码似乎显然出了问题,但是这个模块被很多其他模块调用,这行代码是什么时候,为了什么目的,经过谁签入的呢?如果贸然修改,会不会导致其他问题呢?怎么办?

    答:针对一个源文件的每一行是在什么时候签入,为了什么签入,在github里有非常好的支持,如下图:

     在上图中我们可以看到,Handle10611.tex在两次提交中被修改了,分别是在65b4fe1和9a9913d两次commit中被修改过,并且两次都带有比较清晰的commit日志,知道它是为何被签入,修改又是为了什么。以及所有跟它有关的所有commit提交信息。

    一个良好的团队应当维护一个良好的commit日志,并且有所规范,但是我们组在这一点上还非常欠缺...

    8. 如何给一个系统的所有源文件都打上标签,这样别人可以同步所有有这个标签的文件版本?

       代码每天都在变,有时质量变好,有时变差,我们需要一个 Last Known Good (最后稳定的好版本) 版本,这样新员工就可以同步这个版本,我们如果需要发布,也是从这个版本开始。那么如何标记这个 Last Known Good 版本呢? 

     

    使用git来打tag这件事,在Github中是可以很方便来做这件事:

    每次发布到一定成果后,就需要发布一个realease版本,但是这样的话,是对commit本身打标签。在git里,标签分为两种类型:轻量标签和附注标签。轻量标签是指向提交对象的引用,附注标签则是仓库中的一个独立对象。

    $ git tag v1.0.0

    想查看tag的话,可以使用git tag来查看,如下:

    如果想回到某个标签时某个文件的状态,那么只要使用git checkout tag(标签名) 即可,如下面这个gif所示:

     

    可以看到我们的项目在发布时一共发布了1.1.0,1.1.1,1.0.0三版。

    9. 你的项目的源代码和测试这些代码的单元测试,以及其他测试脚本都是放在一起的么? 修改源代码会确保相应的测试也更新么?你的团队是否能部署自动构建的任务?

        在签入之前,程序员能否自动在自己的机器上运行自动测试,以保证本地修改不会影响整个软件的质量?

        在程序员提交签入之后,服务器上是否有自动测试程序,完成编译,测试,如果成功,就签入,否则,就取消签入?

        团队是否配置了服务器,它自动同步所有文件,自动构建,自动运行相关的单元测试,碰到错误能自动发邮件给团队

    答:配置了服务器,一开始使用的是Travis-CI来自动集成测试,但是由于网络因素,Travis-CI登录很慢,所以最后决意采用了drone.io来进行自动化的单元测试,每次测试都会自动按照预定的脚本运行单元测试,单元测试通过以后可以在Github的ReadMe里体现出来。

    是这样的标志:

    点开build passing的示例,我们可以看到在drone.io中我们部署的自动化测试:

      

    每一次commit都会触发自动化测试,在部署成功后(只针对master分支),已经test了38次。

     在Setting中也可以看到,我们在build出错时,会自动通知我的邮箱(qianlxc@126.com)

  • 相关阅读:
    949. Largest Time for Given Digits
    450. Delete Node in a BST
    983. Minimum Cost For Tickets
    16. 3Sum Closest java solutions
    73. Set Matrix Zeroes java solutions
    347. Top K Frequent Elements java solutions
    215. Kth Largest Element in an Array java solutions
    75. Sort Colors java solutions
    38. Count and Say java solutions
    371. Sum of Two Integers java solutions
  • 原文地址:https://www.cnblogs.com/buaase/p/5066288.html
Copyright © 2020-2023  润新知