欢迎光临BDM
一枚菜鸟码农的成仙之路

大话版本控制

本文适用于:
– 初次接触版本控制的新手
苦于管理文档的文字工作者
期待更好的利用“版本控制软件”的软件开发人员
– ……

为什么要版本控制?

“版本控制”现在被广泛应用于软件开发中,主流的两款软件是 Git 和 SVN。通常意义上所说的“版本控制”,其控制目标是“文档”,一则控制文档的版本顺序,二则同步不同合作人员的文档内容。一切不限于文档形式的文字内容都可以被“版本控制”。在软件开发人员中,可以是“软件的源码”;在新闻行业中,可以是“撰写的稿子”;在学术研究团队中,可以是“待投的论文”。各行各业,涉及到文字的,都有潜在的版本控制的需求——哪怕是最基础的版本管理。

从驳回、修改、重新提交说起

任何涉及到上级审核的内容,难免会有“驳回”的操作——因内容不合适、选材不恰当、三观不正确等。你也不幸被驳回了,于是你只能根据驳回的理由,修修改改,然后怀着敬畏之心,再次提交。漫长地等待审核过后,没想到上级又驳回了你提交的内容,并给出了他的建议:

那个小王啊,你新改的这个第二段还不如之前的呢,就用上个版本的吧。然后你新改的这个第六段上上个版本的第五段很契合,记得把第五段改回去,这样逻辑比较顺畅。

你点了点头,机智地从回收站里按删除时间排序,取出了时间最小的前两个版本,复制了上级钦定的这两段粘贴至你当前的文档中,再次提交。当然最机智的做法是,你早已准备好了一个历史版本的文件夹,用于存放各个版本的文件副本。并标注好了版本号,能够一眼就被你找到什么是“上上个版本”的文件,你轻松地就从中取出想要的版本段落并再次提交了上去。

你按照最机智的做法有条不紊的完成了上级的各种刁难,上级对你非常满意,决定加派一个同事和你一起维护这个文档,减轻你的工作压力。

1 + 1 > 2?

仅你一个人维护时还好,当两个人同时维护时,诸多问题都一一体现出来。

首当其冲的是文档同步问题,你拉了一个讨论组,两个人不断地分享自己修改过的文档,并在另一个人的基础上继续修改,看起来一切还有条不紊地运行。

但是你的文档版本库就比较崩坏了,都别谈放到版本库了,两个人传输了大量的临时文档,你甚至都分不清一段话是谁写的又是谁改的,上级驳回时该交给谁修改,你都很茫然。并且你意识到,这样你们的时间效率只有 0.5 + 0.5 = 1,甚至由于沟通成本效率还不如 1,你硬着头皮决定开始解决这些问题。

合作模式 1.0 —— 解决多人开发下版本管理及文档同步的问题

针对文档版本的问题,你提出了两种方案。一种方案是你拿出了你的宝贝文档版本库全部放到了云盘里,两个人同时访问云盘共享一个文档版本库(主流版本管理软件 SVN 的做法),每次修改文档时,都与版本库的最后一个文档进行整合;或者两边各自撰写文档,各自维护自己的版本库(另一款主流版本管理软件 Git 的做法),每次需要给上级审核前先碰个头,把最后一个版本的文档内容整合一下。

对于文档整合的问题,你考虑了一个最稳妥的办法——逐行对比。这个方法以文档版本库为基础,具体的操作是:除了自己手头上的文档以外,还得从版本库中拿出了另外两份文档:一份是你手头上的文档的初始版本,也就是你做出这些改动前的版本,你得通过与之的对比知道你修改了什么东西;另一份是版本库中最新的版本,也就是对方修改后的版本,你得把你修改的东西应用到这份文档上生成最终的文档。一行一行比对下来,发现自己新增了这些内容,就在对方文档上添上,发现自己删除了一些东西,就把对方的文档里对应内容给删了,以之来形成最终的文档。

虽然日子苦了点,但在这套“版本控制”的文档管理思想下,你终于有条不紊地进行合作。后来再来了很多人同时维护这个文档时,也不需要担心会出这类问题了。

版本控制神教——版本管理思想完善

由于你毕生贯彻着“版本控制”思想,终于有一天,你成为了一方之师。每当后生在你的课堂上问你“我们为什么非得版本控制?”时,你悉心向他教导:“当你对合作模式或者版本回退有需求时,你会明白的”。

因不能再写文档而闲下来创建的“版本控制”神教信众越来越多,但伴随着“逐行对比大师”称号的还有颈椎病、近视眼等日渐加重的顽疾,或许你已经无法成为版本控制的优秀实施者了,渐渐地,你的工作转移到推广“版本控制”思想并解决信众们发现的一些问题上来。

冲突与解决

首先的历史遗留难题是文档修改所导致的冲突,来自于“逐行比对”——这项细致而苛刻的工作。最简单的冲突,还好说,你在文档的这行里添加了几个字,他删了几个字,一比对虽然发现有增有删,但影响的是一行中独立的两句话。于是负责逐行比对的操作人员顺手就给他该删的删,该添的添,虽在修改了同一行,但并不会影响文字的通顺。

但当你在文档的这段里加了一句话,而他却删了整个这一段文字时,矛盾就发生了,你倒底该听他的还是按你自己的来呢?你觉得不能擅自下定论,必须当面对质,不过既然他现在不在,你就把这一段加了一个标记,记为“冲突”,先继续逐行对比接下来的内容,之后再一并解决吧!

第二天你拿出你长长的“冲突”清单,逮着他问:“这些地方是听你的还是听我的?”,他连连点头:“听你的听你的”,你满意地一个一个把冲突的地方变成了你的文字,然后在再加上了一个新的标记:“已解决”,当你检查发现了所有冲突都解决时,你把这个文档上传到了版本库里。

优化文档整合

“逐行对比”带来了诸多身心上的痛楚,你决定优化文档整合的方案,使其不会随着文档内容理所当然地日益增大而加剧对视力及颈椎的危害。吸取了文档冲突的经验,你决定将“标记”玩法运用到文档整合之中:

  1. 在新增的内容部分,打上一个“新增”的标记,并把新增的内容标明出来。
  2. 在删除的内容部分,打上一个“删除”的标记,并把删除的内容标明出来。
  3. 在修改的内容部分,分解成一个“新增”及一个“删除”。

仔细想想,所有的修改都由“新增”和“删除”组成。

在新的整合方式下,整合的操作人员再也不用“逐行对比”了,只需要全篇找“标记”即可。并且是否产生了“冲突”也一目了然:只需要将从修改前的初始版本到我修改后的版本的之间所有“标记”,与从初始版本到版本库最后一版之间“标记”进行对比,如果这些标记标明了同一个文字,必然产生了“冲突”,而不需要再肉眼比对了。你非常满意,同时也很懊恼,为什么这个解决方案没有被早点想出来。

分支与分布式

日益扩大的教众群体中包含某些性格的人数也日益增多——完美主义强迫症患者。他们通常会陷入两难的境地,不知道文档是这样写比较通俗,还是那样写比较优雅,他们很想两种思路同时发展,看看哪种路线走得比较舒服。你也曾经是其中一员,为推广“版本控制”思想是选择大话胡说或是严肃教程而深深纠结过,原本你是在脑子里计算这一切的——判断几种方向最后发展的结局,但现在眼看脑子越来越不好使,你开始想一种方法使脑力得以从中解放出来。

你记起了曾经用过的一种方法——“复制”、“粘贴”。将文档复制一份,这边按这条路线改,那边按那条路线改,最后你看看哪种更满意,然后将其存入版本库。实际上这种方法也总是让人难以满意,源于一个最根本的问题——两边各自发展到什么境地时再进行比较呢?很有可能这个版本刚刚觉得 OK,存入版本库,突然就觉得另一个被废弃的版本也还不错,只好忍痛又改为被废弃的版本。

就像一条河流,在这个地点分叉,各自壮大,又在下一个地点汇集到一起

你决定对版本控制的思想做一次大的改动,让他们能够支持原生支持分路线的版本管理。就像一条河流,不再需要考虑废弃其中一条,他们均在版本库中有所存储,在需要时会汇集到一起,各取每条分支的所长。

实际上,分支的概念也使分布式版本管理(Git)——这种合作团队每人本地保存着一个版本库的形式,日益壮大了起来。在这之前,每人手头上的版本库的“同步”是必不可少的,每个人修改了文档,提交到本地版本库之后,必须也要将这个改动通知给其他所有人。就像一条川流不息的长江,每个人的版本库即为一条它的复制,你在你长江的这个地点汇入一条湘江,就得“同步”到所有长江的复制,让他们也添加一条湘江。

在这之前,这种“同步”一定要在本地的下一次改动之前!否则本地连续改动多个版本后再通知其他人,会致使每个人的版本仓库混乱和不同步。因为其他人是版本库里仅有你的一次提交,而你实际上是提交了多次的,如同你在本地添加了多条河流之后,再一次性通知其他的长江,但其他长江的眼里,只有你的多条小河等价的综合大河,久而久之版本就会混乱。

有了分支的概念后,同步变为了异步,分布式版本管理再也不需要如此捉襟见肘地频繁提交了。每个人本地的版本库就相当于一个分支,不用担心本地连续改动多个版本而会导致混乱,因为现在每一个版本库(分支)的版本信息会完完全全保存下来,而不是只有一条主干。分支的概念解决了面临两难选择的问题,也为合作提供了重要的基础——每个人只需要专注于自己的内容,在需要时进行版本合并即可。

版本控制毒瘤——野蛮生长结果

新时代到来,版本控制软件诞生在了软件开发行业,取代了一切文档撰写人员机械化的整合、比对工作。版本管理的思想的任何细节在软件层面都有相应的结构和实现,均可由机器完成,使版本管理思想的效率达到最大化,并迅速开始回馈于软件开发行业。主流两大思想,集中式版本仓库软件 SVN,和分布式版本仓库的 Git 正式发行了出来,由于轻便易上手,甚至不再需要了解透彻你的“版本管理思想”即能轻松使用,版本管理软件在软件开发行业进入了野蛮生长时代。

猜我提交了什么党

小型合作团队中,在“敏捷开发”的指导思想下,滋生出一批提交无信息党。通常来说,他们都坐在一起,有什么疑问,直接吼一嗓子:“你这块代码是做什么的?”。他们更注重开发效率,而非代码规范、工程结构、代码注释等这些“可有可无”并会降低开发速度的东西,更别提代码提交时的信息——或许不一定知道有这个功能。在这种情况下,难免有年久失修的功能,他们因此早已练就一身看代码理解当年需求的本领,但这种情况非常不多——或许心知肚明小型团队的生命周期吧!

文档云存储党

很多人弄不太清楚为什么要用版本控制时,就开始了版本控制软件的使用,让你很是心疼。其中一大部分,在他们手里,版本控制软件最重要的功能,是提供一个地方保存文档,云保存。他们会在自己开心的时候进行提交,或者是在回家前,这样他们回家后也能拉取到正在撰写一半的文档进行工作。云存储党一般伴随着提交无信息的行为,在合作模式中,若使用集中式版本仓库并且无分支管理,这种行为非常致命,其他人同步到他们开发一半的代码导致整体报错时,由于不了解其中逻辑只能束手无策。哪怕没有报错,也会造就一大堆不明所以的提交记录,难以拼凑出一个完整的功能。

为所欲为强制党

分布式版本控制软件的使用者里,他们应该是对自己最有自信的而洋洋得意的——自从学会了强制功能,便再也不会在自己的版本库里出现问题了。什么因为本地版本库的HEAD与远程HEAD指向不一致而导致提交失败?强制提交!啥啥啥覆盖了远程仓库都不关我的事。什么本地代码与远程代码冲突?强制拉取!等等你为什么要覆盖我的代码?他们可能并不会拥有全部的强制能力,但对“强制”功能的片面的理解能力上是独树一帜的,一定能使你记忆犹新。

谨小慎微保守党

比起上述破坏性的流派,真正对版本控制思想的传播产生威胁的是这一群保守不进取的人。破坏性的行为通常是由于对版本控制软件的片面理解导致,而且造成破坏性的结果能使团队立即对这个人进行处理教育,但保守党们是拒绝对更合理的版本控制使用。他们表现的形式多种多样,总结为一切他不了解的就是错误的,不被允许的,而现实中他们的人数最多,话语权最大,不需要去刻意寻找它们,生活会给你答案。

版本控制灵魂——分支最佳使用探究

伟大的版本控制神教教主曾说过:如果提交合并修改是版本管理的基础,那么分支管理是版本管理的灵魂。

你曾经不止一遍问过自己:到底什么是软件开发行业中版本管理思想的最佳形态呢?之前的你思考的最多的是文档整合过程的优化,现在机器能代替一切。自从加了分支的概念,很多东西都变得不一样了,有的变得繁琐——如文档版本回退,在多分支的情况下容易让人产生困惑;有的变得简单——如分布式的文档库管理,每一个库自己的改动都将是一条对于主干的分支。你决定综合整个令原有思维方式改变的分支思想,来构建最佳形态,于是你提笔在纸上写了起来。

分支是什么?

分支是原有版本的拷贝,是一个大河分出去的两条同样的河流,是一种不确定时的试探,是一种并行发展的思想。分支的概念对应的是主干——最初始的版本发展路线,主干也可称为主分支。分支之间隔绝了任何耦合,任何有问题的情况仅仅在分支合并时暴露。分支在独立发展中,会认为自己就是主干,但任何不完全确定影响的修改都应该在分支上完成,测试完成后,再合并至主干。

分支合并会暴露什么问题?

主要还是冲突问题。实际上版本提交时的微观操作和分支合并没有区别,前者是自己的修改提交到版本库,后者是这个分支提交到“主干”,利用前者解决冲突的思想也可以解决后者合并的问题。

关键是合并本身,分支在发展的过程中,可能和主干的冲突达到一个难以处理的量级,该如何在短时间内解决好这么多的冲突呢?一定要保证在短一定发展后进行同步一次,使分支不至于太离谱。同时保证分支依然是分支,这里明确一下合并的概念:从 A 合并到 B 的结果是 B 中包含了 A 的改动,并且 A 消失,故不能在同步的过程中使分支消失。或许可以这样:在分支的发展过程中,从主干中重新再分出一条分支合并到该分支上,使分支中包含了主干这段时间的改动,并且在分支这边解决好冲突,使将来分支合并回主干的过程更准确高效。

主干的作用是什么?

既然任何修改都应该在分支上完成,那为什么还需要主干呢?不能直接采用去中心化的分支各自为政多线并进的方式吗?一方面呢,是为了避免混乱,肆意生长的分支而没有主干会使整个团队困惑,合并调整也会混乱;另一方面,现实中版本管理的应用场景总有需要主干的地方:如软件行业中的主干一般是软件开发的路线,生产环境必然与主干中某一版本对应;又如分布式的版本管理软件(Git)也需要一个中央服务器来统筹协调,避免单机宕机的情况下无法获取到单机中的重要的版本信息。

所有的分支都以服务于主干为目的,所有分支的修改内容都以增强主干内容为目的,在这种指导思想下,运用起分支来才能清晰灵活。

分支复杂的情况下如何回退版本?

代码库回退一般发生在功能被弃用的情况下——如果纯粹是线上版本出了问题,直接回滚到上一个版本的代码就好了,不需要将整个代码库的版本回退。无分支时代的回退特别简单,直接无视多出来的版本,从历史版本上重新开始版本即可。但在分支复杂的情况下,极有可能当前主干上需要回退的版本,已经被其他分支合并兼容了,那么这时候该如何使其他分支也能回退相应的内容呢?

这种情况从简单到复杂,有很多种情况,这也是分支管理中不可跨越的困难,目前还没有一个完美的解决方案,只能大致总结。对于简单版本的,即需回退版本比较近的情况,可以直接进行回退,回退之后的内容合并至所有受影响的分支上。如果与回退的目标版本中间又添加了新的不回退的版本,则先在这个版本上开启分支,再进行回退,之后把该分支合并过来。对于久远的版本,比较令人接受的做法是把相关的代码直接删除提交成一个新版本,因为这种情况下回退消耗的资源多得多。

理想的分支运用

综上所述,理想的分支运用场景应该是这样的:
1. 当主干(相对主干,可以是分支)上有一个新功能需要开发时/新 BUG 需要修复时,在该版本上新建一个分支。
2. 在该分支上进行修改操作。
3. 待合并回主干时,将主干新建分支合并到当前开发分支上,解决完冲突后,合并至主干。
4. 如果第 3 步时需要其他分支联合,也需要将其他分支新建分支合并到当前开发分支上。

按照这种思路,能保证每条分支上的独立开发的同时,也容易理解。对于身兼多个分支的开发人员,势必要不断切换分支进行开发,可以考虑checkout成多个分支文件夹,这一块在 SVN 上和 Git 上的实现略有区别,暂不在这里引申开来。对于复杂和久远的版本回退,建议采用直接修改代码的方式进行新版本的提交,多分支管理的情况下版本管理的回退功能仅建议在少量版本跨度时进行。

补充:忽略列表及标签

版本控制中一个非常重要的功能就是文件忽略,将文件排除在版本控制之外。每一个版本控制管理下的软件项目,应当把环境变量、运行时生成的文件、不需要上传的本地文件等等类似的和本地开发环境强耦合的内容及时排除在版本控制之外,这些内容往往会影响到其他人的环境或者添加了很多冗余,给版本控制软件增加了负担。SVN 和 Git 都支持通配符进行识别,可以方便的将系列文件排除在外。

标签给版本库的可读性及易搜寻性提供了很多帮助。使用者可以给某个版本添加一个标签,相当于一个别名来标明这个版本,通常被用于正式版本的标记,以及版本的再命名如 V1.0.11 等。SVN 会非常累赘地将该被标签的版本生成一个全量备份,在标签库里,而 Git 仅需添加一个标签即可。

本文遵守知识共享署名-相同方式共享 4.0 国际许可协议,未经允许不得转载BigDickMan » 大话版本控制

相关推荐

  • 暂无文章

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

联系我们GitHub