版本控制学习

现如今,无论是公司代码管理,或是自己独立编程的代码管理,都离不开版本控制工具,使用版本控制工具主要是为了避免或解决一下问题:

1. 保存修改历史。一方面是可以知道每一个节点都做了哪些修改,另一方面是当前版本出错的时候可以随时切换到指定commit上面。

2. 避免冲突。当多个开发人员共同修改同一个文件时,很容易产生修改内容冲突,这是版本控制工具可以告诉我们是谁提交了哪次修改,修改了哪些内容,与我们此次提交的内容有哪些不同。

3. 高效合并、管理代码。当我们合并两个分支时,即使这两个分支都对同一个文件做了修改,但是只要没有发生冲突,它会自动的合并两个分支的修改。我们也可以方便的查看两次提交之间做了哪些修改,分别增加和删除了哪些内容。以及当我们发现我们所使用的分支不是最新的时候,我们就知道有人修改过文件了。

4. 权限控制。可以设置权限,让部分人员只能查看代码而不能修改上传代码,或者直接设置不能查看。

5. 多版本管理。 当一个程序需要面向多个不同的定制化需求时,我们不需要维护多个拷贝,只需要使用版本控制工具维护多个分支即可,其余的事有工具帮我们完成。

目前比较主流的有svn、git等,以前的公司讲究集中管理和权限控制,使用svn管理代码。现在公司使用git管理代码,各有长处,现在习惯使用git来做版本控制。

对于分支管理,比较受认可的用法可以参考http://nvie.com/posts/a-successful-git-branching-model/。然而用正规用法是个后知后觉的过程,都是慢慢的向那边靠拢,合适的才是好的。

选用了gitlab提供git服务。gitlab是在git基础上搭建起来的版本管理系统,它除了包含git的功能外,还提供了很多优秀的web功能。通过gitlab,多个开发人员可以在网页上查看提交历史、分支内容、发起issue和merge request、设置milestone和label、编辑wiki资料、设置权限等功能。就是说从一个项目的开始到完成,中间的每一步,每一个变化甚至每一次讨论都可以找到记录。

当前的使用规范是:

1. 有一个master分支,作为主分支,设为protected不能随意修改。

2. 当有一个新的需求或新的问题。创建一个issue,issue的名字和描述需要尽可能的详细,这样当别的开发人员看到这个issue的时候,能快速的了解以及重现问题。给这个issue设置milestone和label,分配给指定人员。

3. 一个项目一般会有大致的预期时间,或者是有目标功能,这时候可以设置milestone来管理issue。把属于一个时间点或同一个功能的issue放在同一个milestone下,比如网页部分可以设置“webUI”的milestone,如果时间紧迫,可以直接设置“soon”的milestone。这样每次查询未完成issue的时候,可以根据milestone的类目来选择优先级。

4. 一个milestone下面也可能包括多个方面,对系统来说“staging”这个milestone下面可能有后端部分,前端部分等。可以再细分milestone,但是更好的做法是使用label来区分,分别标为“web”、“server”。

5. 开发人员收到分配的issue后,先确定自己理解了内容,“git pull origin master”得到最新代码,再“git co -b newBranchName”,新的分支特定的只针对一个issue,比如issue号是123,新的分支一般都是“bug#123-issueName”。

6. 在新建的分之上修改代码,添加unit test。完成功能之后,“git push origin newBranchName”将新分支提交回gitlab服务器,这是gitlab里面可以看到有若干个分支,每个分支都负责一个issue。

7. 在merge之前,需要有个code review的步骤,这时负责issue的开发者发起一个merge request--“Closes #123 issueName”,这么命名的原因之一是合并之后,gitlab会自动关闭123这个issue。选择code reviewer,发送。

8. code reviewer检查完代码之后,如果没有问题,则完成merge。如果在merge的过程中产生conflict,则需要开发者在本地执行“git rebase origin/master”解决冲突后再提交并发起merge request。

9. 合并完成后,可以删除issue的分支。不同分支做的修改在合并的时候只会合并在此分支上做的修改的部分,就像“git diff HEAD masterID”的内容一样,masterID是当前分支和master分支交汇的commiID。所以,不同分支先合并的先后顺序的不同,不会对最后的结果有影响,最后看到的代码是一样的。

ps. 以后可能会每个milestone都创建一个分支,所有的下属issue先合并到此milestone分支,然后再合并此milestone分支到master分支。

因为负责团队只有两个人,到后期甚至只有一人负责,所以目前来说能满足日常开发要求,但是上述过程有一个确定是只有一个master作为主分支,系统正式上线运行后,面对多个环境“test”、“staging”和“production”会有些麻烦:

理论上来说,版本号是“test”>“staging”>“production”,三个环境都使用的master分支,所以三个环境的commitID是不一样的;而且test环境的代码版本也可能不是master上最新的。

1. 在“test”环境通过测试想要部署到“staging”环境时,需要记住test环境此时的commitID号,再staging环境执行“git reset --hard commitID”更新。同过程在“staging”到“production”一样。

2. 如果“production”正在运行版本的代码遇到突发错误,需要回滚到上一个稳定版本,则还需要记住上一个发布版本的commitID。

解决办法有两个:

1. 创建三个分支,分别对应“test”、“staging”和“production”。每个分支对应各自的环境。

2. 使用tag,对每个环境的每个版本贴上标签。当然也可以这两个一起使用。

基于目前master分支作为唯一的主分支,打算尝试使用tag的方法:

tag的作用简单的说就是标记某一个commitID,因为git里的commit ID是一串很长的无规律字符无法记忆,而tag可以贴上规则的信息(有点像域名和ip地址的关系)。

目标是在只有一个master分支的情况下,管理“test”、“staging”和“production”三个分支。使用tag虚拟出三个分支:

比如“test”分支,在部署新的版本时,使用“git tag -a test_20161130 -m "123123"”打上标签,其中test_20161130是tag名称,这里使用的是日期来标记,也可以使用版本号等方式。

在通过“test”测试之后,需要把“test”环境的版本部署到“staging”进一步测试,这时找到“test”环境最新的一个tag(因为每次部署test环境都会打上一个tag,最新的tag是包含test环境当前系统版本的)。找出这个tag对应的commitID,在“staging”环境下“git reset --hard commitID”下载对应代码。此时,“staging”和“test”的版本是一致的,再对“staging”环境打上tag“git tag -a staging_20161130 -m "123123"”,再上传tag“git push origin staging_20161130”。在“staging”到“production”也是相同的操作,只是在tag的时候加上版本号:“git tag -a prod_v0.1.200_20161130 -m "123123"”。

在经过一段时间之后,系统应该会产生许多个tag,但相比于它带来的管理上的好处,这是可以接受的范围。
突然想到有一个比较严重的例外:如果“production”环境发现了bug怎么办?嗯。。。看来还是需要使用第一种方法,创建3个分支。这。。。看来上面这些白写了:),anyway,还是留着吧,当作心路历程,再说tag也是需要的。

还有一个是多发布版本控制,系统可能会有特殊定制化的需求,也就有可能有多个发布版本。参考github上的netty,使用多分支管理多版本,每个发布版本都拥有自己的分支。

如果有一个改动对多个分支有影响,则可以使用“git cherry-pick commitID”功能,选择性的合并commit。如果有多个commit,使用“git cherry-pick commitID1..commitID2”(1.7.2版本开始支持。。。)。如果发生冲突,则需要先解决冲突内容再提交。

一个少见的情况是:git已经push,想把一个分支退回历史版本。有两个方法:

a)使用git reset:git reset --hard commitID在本地退回到某一历史版本后,再做更新、commit,再push回远程分支。但是这时候会报错,因为push的内容和已有内容是冲突的,需要强制进行。使用git push -f origin branchName。一个人使用gitlab的时候可以这么做,如果有多个人使用,容易造成混乱。

b)git revert commitID:生成一个新的提交来撤销指定的一次提交,所做的操作与指定提交相反。在git push回远程仓库。这样,所有的历史提交都还保留着,多人合作时也容易分辨。

最后在记录一下git提交的总结:

1. 应该经常性的提交,每次提交涉及的改动应该尽可能的小,不要等一个大的功能全部完成了再提交。

2. 在1的基础上,应该保证每次提交都完成了一个小功能,并且是经过了测试的。

3. 如果有特殊原因,需在提交信息中注明是未完成。

原文地址:https://www.cnblogs.com/starRebel/p/6114850.html