Linux环境SVN命令行使用经验总结
2013-04-17 10:04:22   来源:我爱运维网   评论:0 点击:

Linux环境SVN命令行使用经验总结Phil Chen ( philchen@tencent.com)2013.4.15Linux后台服务器编程的开发模式有四种:1. 在windows机器...

Linux后台服务器编程的开发模式有四种:

1.       windows机器上开发得差不多了之后,打包传送到开发机编译,在开发机上解决编译错误。

[缺点] 浪费时间在打包解包,机器间传输代码。

2.       windows机器上开发之后,check in代码进分支,在开发机上check out或者update后,进行编译,解决编译错误。相当于把svn作为一种机器间通信方式。

[缺点] 提交进svn的代码甚至都没有编译过,我只能说,svn不是这样用的。

3.       linux开发机上安装samba, windows环境下开发,开发机上编译。

[优点] windows环境编译,很便捷;可以实时在开发机上编译。

[缺点] 如果不用命令行,TortoiseSVNsamba下慢得不能忍。

4.       完全在Linux开发机上开发,编译。

[优点] 反馈更敏捷,命令行都已经提交完代码了,乌龟svn可能还在龟速更新。

[缺点] 缺少图形界面, svn操作完全是命令行的,不太直观。

 

通常大多数习惯前三种模式。在windows环境使用TortoiseSVN, 各种操作都封装的很好,使用起来也很傻瓜。最近在linux环境下开发,发现svn命令行用熟了比TortoiseSVN更好用,最重要的是反应速度更快。

 

我们的svn工作模式

SVN并行开发管理策略

总的原则:trunk保证相对稳定。分支合并到主干时将冲突降至最低。

(1)       trunk用于集成、测试、发布,可以提交fixbug代码,但不允许直接提交新特性。

(2)       特性在分支上开发,在编译、测试通过后才能合并到主干。

(3)       特性分支确定一个负责人,负责每天执行从trunk到分支的合并。合并回trunk前,先执行一次trunkdev的合并,然后在trunk上使用复兴分支。

(4)       特性分支的存在时间不能太长,不超过一周为宜。合入主干后不能继续使用。

相册特性开发-采用特性分支

Linux环境SVN命令行使用经验总结

特性分支: 为了使新特性的开发不影响测试或发布,把新特性开发放在分支上进行管理,分支的生命周期取决于特性的开发周期,该策略的特点是:

1  trunk用于集成、测试、发布

2  新特性放在分支上开发

3  分支负责人定期(如每天)把trunk的变更同步至分支

4  分支合并回trunk前先执行trunk到分支的合并,然后使用复兴分支

5  分支合并回trunk后,分支生命周期结束,清理分支。

第三方代码管理-采用供应商分支

Linux环境SVN命令行使用经验总结

这里的供应商分支通常是指对第三方源代码的管理,供应商分支的管理通常步骤如下:

1  创建供应商分支目录,导入供应商代码,供应商代码的维护在此分支上进行

2  将供应商分支拷贝(branch/tag方式)到trunk或开发分支的某个目录下

3  trunk或开发分支不对供应商分支代码做修改

4  trunk或开发分支确认使用供应商分支的新版本时,把供应商分支合并到trunk或开发分支

 

可能会有人存在疑问,为什么要对供应商内容做管理?

 

供应商代码和自己的产品代码可以看着是两个产品线,供应商的代码可能随时会发生变化,为了不让供应商代码的变更影响自己产品的正常研发,就有必要使用分支独立管理供应商代码。

 

供应商分支还可以被另外一种方案所替代,那就是:外部引用(svn:externals

分支合并到主干策略

如果说到svn使用最痛苦的一点,那莫过于主干合分支,或者分支合主干时爆发的各种莫名其妙的冲突狂潮,如“树冲突”,“文件冲突”,“文件丢失”等等。解决起来费时费力,还容易出错,效率很低。有没有较好的解决方法,可以让日子好过一点呢?答案是有的,需要遵循下面的方法。

冲突产生的根源

svn版本管理原理

svn生成的每个版本只记录增量修改,每个文件都有一个根源版本。

根源版本

 

Linux环境SVN命令行使用经验总结
根源版本,即祖先版本,在合并的时候是比较重要的一个概念,类似于C++中基类和派生类的关系,这里所说的根源就是一个基础版本。

如上图所示,hello.hv1版本是v2版本的根源,而v2则是v3v4的根源。

当进行分支合并时,SVN会沿着分支和trunk追溯共同的根源版本,然后计算其差异,如果没有共同的根源,则会报树冲突,就只能使用忽略根源的方式进行合并了。

 

冲突1:丢失文件及可避免的树冲突(可以避免)

 

当多人协作开发时,如果分支间合并缺乏时序性,就有可能导致合并时丢失文件。

举个简单例子来说:

Linux环境SVN命令行使用经验总结

如上图所示:

AB两人同时在2.0-dev分支上开发,A新增一个a.cpp,并提交到SVN版本库上,产生123版本;然后B基于A提交的a.cpp做修改,提交后生成一个新版本124

如果B此时想把自己修改的版本124mergetrunk,那么问题就来了,因为124版本的根源版本是123,只合并124过来,实际上就相当于只合并124的变更内容,而此时trunk上还没有a.cpp,这时SVN就会认为trunk上的a.cpp被删除了(因为找不到),会提示用户一个树冲突;

【由于是从2.0-dev合并到trunk trunk是主合并方,分支是从的关系(在SVN中,trunk的东西称着mine,而分支是theirs),所以处理树冲突的时候优先用mine来解决】

124合并到trunk时,由于找不到a.cppSVN会报一个树冲突,用trunk解决冲突,实际上也就是不会把a.cpp合并过来,这就会导致合并时出现丢失文件的现象。

 

冲突2:文本冲突(无法避免)

AB两人同时修改一个文件的同一行时,工具不能判断到底是选择A的修改or选择B的修改,或者是AB的修改都要,这个事情就会报一个冲突,这种冲突也就是文本冲突。

 

冲突3:树冲突 (无法避免)

AB同时改了一个文件,A删除了该文件,而B修改了该文件,这时由于二者的修改无法进行合并,所以只能让用户二选一,要么删除该文件、要么保留该文件,这种冲突是树冲突。

树冲突产生的场景比较多,以下AB的任何一种操作组合都会导致树冲突。

 

Linux环境SVN命令行使用经验总结

推荐的合并实践

1)  创建分支时,在日志中写明主干当前版本号。

2)  创建分支后,每天同步一次主干到分支, 并在日志中写明合入的主干当前版本号,便于定位问题。合并命令的用法见小节《常用svn命令》

每天合并问题最容易定位与解决,这也应了敏捷开发中小步快跑的思想,以及持续集成的初衷都是一样的;如果这个频率不能保证,每周也至少2-3次,否则解决冲突与定位问题的代价就大了。

3)合并回主干前,先执行一次主干到分支的合并,然后在主干上使用复兴分支。复兴分支使用了合并跟踪技术(svn1.5以上),能够不指定合并版本范围就将分支中的改动自动合入主干。

4)特性分支的存在时间不能太长,不超过一周为宜。合入主干后不能继续使用,否则当下次从trunk合到此分支时,会引入大量的树冲突。

 

 

 

svn常见使用流程讲解

命令行下字符编码的问题:目前相册源程序文件使用的都是gbk编码,而svn提交时默认使用utf8编码。我们希望在终端使用GBK编码,包括编译调试,但是使用svn时切换到utf8编码。所以需要如下两个封装的命令

 

命令

.bashrc中加入下列两行,并且执行source ~/.bashrc

alias svncoding="export LC_ALL=\"en_US.UTF-8\""

alias gcccoding="export LC_ALL=\"en_US.ISO-8859-1\""

作用

编码方式切换

示例

svncoding切换到svn需要的编码(utf-8

gcccoding切换到开发、编译需要的编码(ISO-8859-1编码无法表示编码,但是开发机对应的默认中文显示用gbk。原因何在?开发机/etc/sysconfig/language里写的是RC_LANG="en_US.UTF-8"secureCRT中使用默认设置(windows默认GBK),看来是以终端编码为准)

结果

 

 

命令

.bashrc中加入下列两行,并且执行source ~/.bashrc

export PHOTO_REPO=

” https://tc-svn.tencent.com/isd/isd_qzoneplatformdev_rep/QzonePhoto_proj”

作用

代表相册代码svn库地址,便于快捷输入,减少输入量

示例

echo $PHOTO_REPO/trunk

结果

https://tc-svn.tencent.com/isd/isd_qzoneplatformdev_rep/QzonePhoto_pro/trunk

 

 

命令

svn info TRUNK_URL

作用

获取svn库的最新信息,如trunk的当前版本号

示例

svn info $PHOTO_REPO/trunk

结果

Path: trunk

URL: https://tc-svn.tencent.com/isd/isd_qzoneplatformdev_rep/QzonePhoto_proj/trunk

Repository Root: https://tc-svn.tencent.com/isd/isd_qzoneplatformdev_rep

Repository UUID: df2599cb-4e79-6a4a-84f3-a1dd8b66c2e7

Revision: 166688 <- 最新版本号

Node Kind: directory

Last Changed Author: reanshen

Last Changed Rev: 166688

Last Changed Date: 2013-04-12 19:07:17 +0800 (Fri, 12 Apr 2013)

 

 

命令

svn copy TRUNK_URL BRANCH_URL –m ”Create branch from trunk %REVISION%”

作用

创建分支

示例

svn copy $PHOTO_REPO/trunk $PHOTO_REPO/branches/photo_xxxx -m “Create branch from trunk 166688”

结果

Committed revision 166690.

 

 

命令

svn co SVN_URL[@REVISION]  [PATH]

作用

svn库中的代码检出到本地,进行开发,编译

SVN_URL: 想要检出的代码URL路径

REVISION: 想要检出的版本号

PATH: 想要将检出代码存放在何处。不填,则将URL最后一段作为文件夹名

示例

svn co $PHOTO_REPO/branches/photo_xxxx

结果

检出代码到当前目录下的photo_xxxx

 

命令

svn commit [PATH] –m “comments”

作用

提交开发的代码

示例

svn commit . –m “add test cases”

结果

提交了当前目录内的修改

 

命令

svn merge TRUNK_URL [–dry-run]

作用

将主干改动同步到分支。svn1.5引入“合并跟踪”技术,让我们不再需要每次自己查找哪些版本需要同步到分支。svnbook中称这种合并技术为sync-merge。对于之前需要手动跟踪版本合并范围,效率的提升不是一点点。

前置条件:本地代码库保证无任何未提交改动(用svn status查看);使用svn update更新到分支最新版本。

--dry-run选项:可以尝试合并,但不修改本地代码。可以借此先看看冲突多不多。

不加—dry-run选项: 进行真实的合并,修改本地代码,需要进行提交。

示例

svn merge  $PHOTO_REPO/trunk

结果

将主干上次合并后的改动自动同步到分支

 

命令

svn merge –reintegrate BRANCH_URL

作用

将分支代码合并进主干。

前置条件:主干中所有的更改都已合并入分支。主干的本地代码库无未提交的代码,并且使用svn update更新到最新版本。

示例

svn merge –reintegrate $PHOTO_REPO/branches/photo_xxxx

结果

分支中所有的改动均合入主干。

无冲突时,OK,顺利合并;

有冲突时,如果是文本冲突,有两种选择。

1.       输入”l”启动配置的merge工具(详见”svn配置一节)

会用vimdiff打开左中右三个垂直并列窗口

左:base版本(分支svn库中的版本)

中:mine版本(有本地修改的版本)

右:theirs版本(主干上的版本)

2.       输入”p”推迟解决。之后用status可以看到哪些文件有冲突。

如果是树冲突,用svn status可以看出来。

$ svn status

	A  +  C code/bar.c
	      >   local edit, incoming delete upon update
	Summary of conflicts:
	  Tree conflicts: 1

如何解决树冲突,视乎具体情况而定。

例如:主干上该文件被删除,则在分支上先使用svn delete删掉文件,再用svn resolve –accept working 解决冲突。

 

命令

svn delete BRANCH_URL –m “remove branch, reintegrated with trunk in r%REVISION%”

作用

分支合入主干后就不能继续使用,否则再次从主干合入分支会造成大量树冲突。

清理掉svn数据库中的分支,让代码库更整洁一些,避免隐患。

 

示例

svn delete $PHOTO_REPO/branches/photo_xxxx  \

-m "remove branch, reintegrated with trunk in r16666"

结果

Committed revision 166702.

分支从版本库中删除

 

命令

svn copy BRANCH_URL@LAST_REVISION BRANCH_URL –m “resurrect branch photo_xxxx@%LAST_REVISION”

作用

这时候有人问了,“万一我需要看某个分支改了哪些东西,删掉分支后我怎么看呢”?

不用担心,分支一旦出现就永远不会消失,我们只是让它隐藏而已。真的需要时,可以轻易让它复活。如此命令所示,其中分支最后存在的版本号可以通过svn log branches父目录获得

示例

svn copy

$PHOTO_REPO/branches/photo_xxxx@166701 $PHOTO_REPO/branches/photo_xxxx -m "resurrect branch photo_xxxx@166701"

结果

复活分支

 

大体的svn工作流程如上所示,下面是其他一些通用操作。

命令

svn update

作用

svn库中的代码更新到本地代码库

示例

svn up

结果

 

 

 

命令

svn resolve –accept [working | theirs-full | mine-full |base] filename

作用

冲突解决

--accept是个选择器

base: 选择上次最新checkout的版本(未作本地修改的)

mine-full: 选择仅包含自己本地修改的版本,忽略来自服务器的代码改动

theirs-full: 选择仅包含来自服务器的代码改动,忽略自己本地的改动

working: 选择手动merge完成后的版本

示例

 

结果

 

 

命令

svn status [-v]

作用

查看本地文件状态,可以看到当前目录下所有的改动

-v: 可以显示所有的文件信息,包括未修改的。

-u: 可以用”*”标识有更新的文件

示例

svn status

结果

?       scratch.c (不在版本控制中)

A       stuff/loot (新加文件,未提交)

A       stuff/loot/new.c

D       stuff/old.c (删除文件,未提交)

M       bar.c (修改文件,未提交)

C        cc.c (有冲突未解决的)

 

命令

svn log . –stop-on-copy –l N [-v]

作用

查看svn记录

--stop-on-copy: 在分支从主干分出时停止

-l N: 显示最多N个版本的Log

-v: 显示详细的改动(文件改动)

示例

svn log . –stop-on-copy –l 1 -v

结果

------------------------------------------------------------------------

r164717 | philchen| 2013-03-29 09:22:23 +0800 (Fri, 29 Mar 2013) | 2 lines

Changed paths:

   M /QzonePhoto_proj/branches/photobuild_xxxx/application/qqphoto/comm/photo_log/include/monitor_id_define.h

 

命令

svn diff [filename]

作用

未指定filename: 查看当前目录的所有更改

指定filename: 查看指定文件的修改

示例

svn diff

结果

如果未配置diff-cmd, 默认使用svn自带的diff工具,输出格式如下

Index: bar.c

===================================================================

--- bar.c    (revision 3) [‘-‘标识前一版本的]

+++ bar.c (working copy) [‘+’标识当前本地文件的]

@@ -1,6 +1,7 @@[解释:’-‘标识前一版本,”16”指显示从第一行开始,共显示6行;’+’标识当前版本,”1,7”指显示从第一行开始,共显示7]

#include<stdlib.h>   [前面无标注,为公共行]

+#include <stdio.h>   [前面为’+’,为当前本地文件的改动]

 

 int main(void) {

-  printf("Sixty-four slices of American Cheese...\n"); [前面为’-‘, 为前一版本的改动]

+  printf("Sixty-five slices of American Cheese...\n");

 return 0;

 }

 

如果配置了diff-cmd, 就使用配置的。详见”svn配置一节。

 

命令

svn add FOO

作用

FOO(文件,目录,符号链接)加入到版本库。添加目录时目录下面的所有东西都被加进来,除非文件的后缀在svn::ignore里面。

示例

svn add newproj/

结果

 

 

命令

svn delete FOO

作用

FOO(文件,目录,符号链接)从版本库中删除。文件或者符号链接会立即从文件系统中删除。

示例

svn del newproj/

结果

 

 

svn配置

配置diff工具(vimdiff

作用:在命令行近似于ui方式较直观显示差异

打开~/.subversion/config, 编译diff-cmd选项。

diff-cmd = /usr/local/py/diffwrap.py

 

diffwrap.py内容如下

#!/usr/bin/env python

import sys

import os

 

DIFF = "/usr/local/bin/vimdiff"

 

# Subversion provides the paths we need as the last two parameters.

LEFT  = sys.argv[-2]

RIGHT = sys.argv[-1]

# Call the diff command (change the following line to make sense for

# your diff program).

cmd = [DIFF, LEFT, RIGHT]

os.execv(cmd[0], cmd)

 

当使用一个外部的diff命令时,Subversion会生成一个非常复杂的命令行。第一个参数就是具体的--diff-cmd,然后就是具体的 --extensions (尽管使用空白的 --符号时会忽略扩展),或者如果没有指定--extensions或者--extensions为空的话,就加上‘-u’参数。第三和第四个参 数,Subversion会传递一个“-L”还有第一个文件的标签(例如,“"project_issues.html (revision 11209)”)。第五个和第六个就是另一个“-L”和第二个文件的标签。第七和第八个参数分别是第一个和第二个文件的名称(例 如,“.svn/text-base/project_issues.html.svn-base”“.svn/tmp /project_issues.html.tmp”)。

 

 

配置merge工具(vimdiff

作用:在命令行近似于ui方式较直观显示差异

左:base版本

中:merged编辑区

右:来自外部的版本

打开~/.subversion/config, 编译merge-tool-cmd选项。

merge-tool-cmd=”/usr/local/py/mymerge.py”

然后编辑mymerge.py

#!/usr/bin/env python

 

import sys

import shutil

import subprocess

 

try:

# Configure your favorite merge program here.

MERGE = “/usr/local/bin/vimdiff”

   

# Get the paths provided by Subversion.

BASE   = sys.argv[1]

THEIRS = sys.argv[2]

MINE   = sys.argv[3]

MERGED = sys.argv[4]

 

# Replace ‘merged’ file with a copy of ‘mine’

shutil.copy(MINE, MERGED)

   

cmd = [MERGE, BASE, MERGED, THEIRS]

subprocess.check_call(cmd)

except:

sys.exit(1)

 

vimdiff用法

 

窗口焦点切换,即切换当前窗口

CTRL-w h 跳转到左边的窗口

CTRL-w j 跳转到下面的窗口

CTRL-w k 跳转到上面的窗口

CTRL-w l 跳转到右边的窗口

CTRL-w t 跳转到最顶上的窗口

CTRL-w b 跳转到最底下的窗口

CTRL-w w 跳转到另一个窗口

CTRL-w CTRL-w 跳转到另一个窗口,同CTRL-w w

光标移动

移动光标,切分窗口会同步移动,使用:set noscrollbind命令可取消同步

]c 跳到下一个不同的地方

[c 跳到上一个不同的地方

上下文折叠

默认情况下,vimdiff会将文件中不同之处上下6行之外的相同文本折叠隐藏,可通过 :set diffopt=context:3 修改显示的上下文行数。

zo 打开折叠

zc 关闭折叠

文件合并

dp 将当前窗口光标位置处的内容复制到另一窗口

do 将另一窗口光标位置处的内容复制到当前窗口

diffupdate 重新比较两个文件,如果手动修改文件的话有时不会自动同步

文件操作

yy 复制当前行

nyy 复制当前行开始的n

dd 删除当前行

ndd 删除当前行开始的n

p 粘贴

u 撤销

CTRL-r 重复(即取消撤销)

wa 全部保存

wqa 全部保存后退出

qa 全部退出

qa! 全部强制退出,不保存文件修改

 

 

 

相关热词搜索:Linux SVN 命令行

上一篇:手记:Linux下成功安装SVN独立服务器
下一篇:基于inotify+rsync的文件实时同步

分享到: 收藏
iTechClub广告