SVN命令用法:程序员的场景

SVN有不少命令,其实常用的也就那么几个,可以结合下实际的使用场景,来说明下SVN的命令用法。

当然可能对很多人来说,最实用的熟悉方式,就是直接运行

svn help (?, h)

就入门了,但为了更好的记忆,有个实际场景也是个不错的选择。

注解

括号中的是该命令的缩写或别名,有的可以少打几个字母,后面也有类似描述。

“新人报道”

你刚入职一家公司,或新加入某个团队,立马参与到一个项目中,项目代号Norther,那么就得获取项目代码,开始你的项目生涯。这个时候一般你需要签出项目代码:

svn checkout (co) https://scms.ship.it/svn/norther/trunk norther

确认工作目录的SVN信息,说明已经纳入版本控制了:

cd ~/projects/norther
svn info

确认没问题了,就运行项目中的构建脚本,然后就可以熟悉代码,展开具体工作了。

当然,有的时候,有一个新项目是由你发起的,你要将初始化的项目工程放到SVN版本仓库中:

svn import souther https://scms.ship.it/svn

确认项目已经在版本仓库中了:

svn list (ls) https://scms.ship.it/svn/souther/trunk

应该就可以看到Souther项目的根目录结构了。

日常工作

当你已经逐渐融入了一个项目,可能一天的工作场景或完成某个任务的工作周期是这样的:

更新

无论是清早或下午或晚上,开始了你的一天工作,你首先会更新你的工作目录:

cd ~/projects/norther
svn update (up)

这样你就可以在最新的项目代码基础上工作了。

修改

可能你写了一个新的模块,需要纳入项目的版本控制:

svn add tools.py

可能你发现某个模块已经陈旧了,不再使用了:

svn delete (del, remove, rm) utils.py

可能你发现一个模块的命名不太合理,需要改名:

svn move (mv) model.py models.py

可能你要创建一个新的较大的模块,需要归档为目录的方式:

svn mkdir groups

可能你发现要写的模块代码布局类似于旧的模块,直接复制个代码模版:

svn copy (cp) users/tests.py groups/tests.py

当然,其实最常见的情况其实还是打开编辑器,比如Vim,修改已经存在的代码,这个就跟SVN命令无关了。

检查

忙碌的一天过去了,或者一个任务完成了,这个时候一般会将你的工作成果,也就是代码更新到版本仓库。

习惯上会先检查下修改状态:

svn status (stat, st)

看到一些SVN状态位信息,确认是修改了哪些文件,之后一般会自己code review一下代码的改动,可能有的人会习惯直接用SVN方式来查看:

svn diff (di)

然后本地运行下相关的单元测试,确认是否有问题。一般来说这个时候,没有什么特殊情况,就直接进入“提交”阶段了,然后结束一个工作日或工作周期,但难免会有些特殊情况出现。

取消修改

当你code review完后,发现有些改动不满意,你可能又会取消这些修改:

svn revert main.py

解决冲突

当你打算提交时候,习惯上一般会再次更新自己的工作目录,现在合并下别人的工作成果(如果有的话):

svn update (up)
  • 可能这个时候更新完代码,你对某个模块的代码有改动,别人也改动了同一个模块的代码,可能就会产生代码冲突。
  • 也可能有的人没这习惯,就直接提交代码,发现提交没有成功,一看,原来是别人提交的代码刚好也改动了你提交的代码,也产生了冲突。

无论哪种情况,就是代码冲突了,需要解决冲突,一般会人工确认代码合并,处理冲突的代码,是选择别人的处理,还是自己的处理,还是要额外处理,处理完毕后,执行命令,比如:

svn resolve main.py --accept working

另外,也有个resolved命令,用来删除“冲突”状态,但官方说被上面命令替换了,不推荐使用了:

svn resolved main.py

提交

最后,一切确认没问题了:code review完毕,自己觉得代码满意了;然后也合并完别人的修改并且没有冲突了;运行单元测试也通过了。那么就提交代码吧:

svn commit (ci)

在分支工作

源代码的管理和发布:以SVN为例这篇文章中,介绍的SVN开发模式中,涉及分支的概念,一般来说会有以下3种情况:

  • 贡献新特性。也就是说,为了增加新的功能,或者对旧功能的改进等等。
  • “除虫”。就是日常说的缺陷修复。
  • 发布阶段(发布分支)->旧版本维护(旧版本维护分支)。这个概念稍微复杂,trunk研发到某个阶段,代码符合某个版本发布条件了,就会新建1个发布分支,测试没问题了,就在这个分支上进行发布;发布完成后,这个版本的维护就在这个维护分支上进行了;这个时候trunk已经进行最新版本的研发了,所以说这个分支是个旧版本维护分支。

上述说的3种分支情况,前两个分支的生命周期比较短,新特性搞定或“除虫”完毕,合并代码到trunk后就结束自己的生命周期了。

最后一种情况,生命周期相对较长,如果这个分支需要维护的版本还要支持,那么就得一直存在,直到不再维护为止。

下面说下在分支工作的实际场景,按顺序:

创建新分支

当上述3种场景发生,确定要新开个分支来写代码,先复制trunk到分支,这里以贡献新特性为例子:

svn copy (cp) https://scms.ship.it/svn/norther/trunk https://scms.ship.it/svn/norther/branches/feature1

切换到新分支

一般来说这个时候本地的工作目录是trunk,确定本地工作目录是干净的,为后续在分支工作,以及合并分支做好准备,避免可能的各种代码冲突或工作成果代码被覆盖等情况出现。

确认当前所在的SVN工作目录,比如,可能是在trunk的SVN路径:

svn info

确认工作目录干净:

svn status (st)

切换到刚才新创建的分支:

svn switch (sw) https://scms.ship.it/svn/norther/branches/feature1

确认切换后的SVN工作目录,应该就是在刚才新创建的分支的SVN路径了:

svn info

在新分支工作

类似,“日常工作”中的工作周期操作,这个时候,你就可以在新分支中进行大刀阔斧的工作了,直到分支中代码符合合并到trunk的的条件了。

合并分支到trunk

在分支中工作一段时间后,确认相关的功能代码、测试代码、文档等都提交完毕了,单元测试通过,大家code review一致认为没问题,审核通过,最后该分支的持续集成(CI)完整build通过。这个时候,就可以进行合并到trunk的操作了。

确保下面操作是在工作目录的根目录下进行

cd ~/projects/norther/

确认分支工作目录干净,没有需要提交的代码了:

svn status (st)

切换工作目录回trunk,如果由于代码变动大有冲突,就解决冲突,特别如果有目录变动很可能有目录冲突:

svn switch (sw) https://scms.ship.it/svn/norther/trunk

确认切换后的SVN工作目录是trunk:

svn info

先在本地合并分支的代码,合并过程可能会有代码冲突,解决冲突,合并会指定版本范围,一般都是分支建立时候的版本号到分支工作完毕时候最后一次提交的版本号:

svn merge -r9527:9549 https://scms.ship.it/svn/norther/branches/feature1 .

确认本地代码变更,code review一下,执行下单元测试:

svn status (st)
svn diff (di)

确认代码没问题,正式提交代码到trunk,SVN的提交日志说明下合并信息:

svn commit (ci)

删除分支

如果确认工作完毕的分支不再需要了,那就记得及时清理掉:

svn delete (del, remove, rm) https://scms.ship.it/svn/norther/branches/feature1

Ship it

在上面说的发布分支工作一段时间后,并且测试完毕,大家觉得符合发布条件了。终于可以进入到版本发布阶段的工作了。

具体故事场景可以看源代码的管理和发布:以SVN为例这篇文章,有对“发布分支”的介绍。

一般来说这个时候已经将trunk复制一份到了发布分支了:

svn copy (cp) https://scms.ship.it/svn/norther/trunk https://scms.ship.it/branches/1.0.x

打标签

复制最新的发布分支为标签:

svn copy (cp) https://scms.ship.it/svn/norther/branches/1.0.x https://scms.ship.it/svn/norther/tags/1.0.0

正式发布

发布又是一个比较复杂的主题,比如:能快速发布、快速回滚(包括数据回滚)、灰度发布等等,在构建发布工具中会详细进行介绍,这里就简单罗列下。

情况1:完整包。导出代码,然后执行打包命令,进行完整安装:

svn export https://scms.ship.it/svn/norther/tags/1.0.0 norther

情况2:补丁升级包。相对复杂,可能会综合运用下列命令,制作补丁安装升级包:

svn status (st)
svn diff (di)
svn patch

情况3:线上更新。一般不太推荐,需要注意不要泄露“.svn”,特别是旧版本的SVN,每个目录下都有“.svn”。可能会用到下列命令:

svn update (up)
svn switch (sw) https://scms.ship.it/svn/norther/tags/1.0.0

一般来说,根据实际情况,可以记录下来发布相关的操作过程。很多环节可以写脚本将原来的人工操作改成自动化操作。以后只要执行发布脚本执行一键发布就可以了。

其它场景

可能还有很多别的场景,比较零散,但也算经常用到。

code review查看代码,要知道对应代码是由谁写的,好询问了解具体代码的思路:

svn blame (praise, annotate, ann)

跟踪问题时候,会查看日志,更方便历史代码定位:

svn log

经常碰到代码锁定或很多诡异情况:

svn cleanup

编辑特定属性,比如:定义忽略规则;依赖其它SVN路径等等

svn propedit (pedit, pe) svn:ignore .
svn propedit (pedit, pe) svn:externals .

SVN客户端更新,使用新的SVN客户端了,有时候会发现本地工作目录SVN相关信息陈旧,会提示你升级:

svn upgrade

好习惯

这里顺带说下几个使用SVN的好习惯,但有的其实跟SVN联系也不算大,只是顺带提下:

  • 保持工作目录干净。或者说工作目录中的代码变更就为了完成一个任务,即一次只做一件事。完成任务后,就直接svn commit (ci)提交到版本仓库,而不用担心其它任务作出的代码变更无提交。并且,对于分支和trunk间切换更方便,而不用担心代码被覆盖或冲突的问题。
  • SVN的日志信息足够有效。足够有效的意思,是说这次提交作出的变更摘要,只要别人阅读了日志就能知道大概,如果为了深入了解变更细节才会去查看具体代码变更。
  • svn commit (ci)前先svn update (up)。可能不更新提交也没问题,但也有可能会相关代码被别人改动了,而提交不了,为了避免这种情况,先本地更新完毕,确保别人的改动不影响你对代码修改的意图。
  • svn commit (ci)前code review。code review本身就是个好习惯,提交前确认是一种更为严谨的方式,如果觉得自己code review发现不了什么问题,那么随便从身边抓个会代码的,跟别人讲解下代码变更的内容,说不定会发现你没考虑到的问题。
  • svn commit (ci)前跑单元测试。写单元测试本身也是个不错的习惯,如果项目本身已经有了完备的单元测试覆盖了,那么你对代码的修改,应该能通过单元测试,所以提交前执行一遍是否通过。如果没通过,就得看下是功能代码写的有问题,还是测试代码有问题,比如:功能需求或接口设计有变化,而测试代码没有同步更新。
  • 有代码变更及时提交。有SVN这种版本控制工具,本身就是为了记录研发过程,避免意外导致代码丢失,如果为了完成某个任务需要很长时间,代码也很久没有提交,风险太高。这个时候,一般会自己开个分支,而将代码提交到分支中,既解决代码要及时提交的问题,又解决代码提交频繁,可能造成代码不稳定影响别人的问题,因为那个分支只有你自己在工作。

最后

这些场景覆盖的SVN命令其实很有限,如果要完整的熟悉,那就svn help以及阅读下SVN的官方手册,有个系统的学习,基础才会更加牢固。

后续

另外,这里只是以程序员的场景来简单介绍SVN使用,对于系统管理员,可能有一部分职责是作为SVN版本仓库管理员,日常也会遇到的各种场景吧,后续也会简单介绍。