##### Linux系统中SVN学习地址:http://www.imooc.com/video/15061
##### Window系统中SVN学习地址:http://www.imooc.com/learn/109
| 特性 | CVS | SVN | GIT |
| ---- | ----- | ---- | ---- |
| 并发修改 | 支持 | 支持 | 支持 |
| 并发提交 | 不支持 | 支持 | 支持 |
| 历史轨迹 | 不支持更名 | 支持更名 | 支持更名 |
| 分布式 | 不支持 | 不支持 | 支持 |
```
并发修改:多个开发人员同时对同一个文件修改,最重要,最常用
```
```
并发提交:CVS只能一个一个提交,分别记录版本号;而SVN和GIT支持同时提交,整合在一个版本号里
这里CVS占内存但是方便找bug,而SVN和GIT正好相反
```
```
分布式:指的是是否需要一台专门的服务器来运行版本控制器。CVS和SVN需要专门的服务器,而GIT是分布式,不需要专门的服务器来运行版本控制器,各开发人员的电脑组成的网络就可以运行git,适合源代码的发布和交流
```
## SVN服务端命令
```
svnserve——控制svn系统服务的启动等
svnadmin——版本库的创建/导出/导入/删除等
svnlook——查看版本库的信息等
```
## 客户端命令
```
svn——版本库的检出/更新/提交/重定向等
```
## 版本库的创建与删除
### 创建版本库
```
svnadmin create/path/repos // path是版本库路径,repos是版本库名称
```
### 删除版本库
```
rm -rvf/path/repos // path是版本库路径,repos是版本库名称
```
### 配置文件位于 /path/repos/conf/
```
authz // 配置用户组及用户组权限
passwd // 配置用户名和密码
svnserve.conf // 配置默认权限、权限配置文件及密码配置文件
svnserve.conf配置文件如下:
vim svnserve.conf i
// 权限分为三种:none(没有权限)、read(允许更新代码)、write(允许提交代码)
将以下两行代码解掉#注释
anon-access = read // 没验证的用户允许更新代码
auth-access = write // 没验证的用户允许提交代码
注:可以根据需求修改权限
password-db = passwd // 指定用户名和用户密码配置文件的路径
authz-db = authz // 指定权限分组配置的路径
注:以上两个路径默认就好,也可以修改
passwd配置文件如下
vim passwd i
可以添加用户
Syears = 123456 // 用户名为Syears、密码为123456
注:可以添加多个
authz配置文件如下
vim authz i
[group] // 组
pm = Syears // 组名 = 用户名(项目经理组)
dev = Syears1,Syears2 // 组名 = 用户列表(用户与用户之间用,隔开)(开发组)
rcookie = Syears3 // (菜鸟组)
[/] // 版本库的根目录
@pm = rw // 对应pm的权限:可提交可修改(项目经理)
@dev = r // 对应dev的权限:只能修改(开发组)
@rookie = r // 对应rookie的权限:只能修改(菜鸟组)
[Syears:/] // 权限只适用于名字为Syears的版本库
@pm = rw
Syears1 = rw // 给指定的用户添加权限
Syears1 = r
Syears1 =
[repos:/xxx]:指定版本库下的某个目录的权限
* = r // 所有用户的权限均为r
注意:创建以后记得修改配置文件,在上面有笔记,需要注意的是,必须在创建版本库之前、运行版本库之后修改其配置文件,否则不会生效,如果运行了以后才发现忘记配置,可以使用如下命令:
killall svnserve // 关闭所有正在运行的版本库
```
# 注意
```
update是指将SVN服务端上的代码更新到本地
而本地代码上传到SVN服务端是commit
```
### 运行SVN服务端
```
svnserve -d -r /svnroot/Syears
```
### SVN服务开机自启
```
vim /etc/rc.local i
在exit0前添加:svnserve -d -r /svnroot/imooc/
```
### checkout和export的区别
```
checkout检出会生成.svn隐藏文件夹,他记录着工作副本最后一次更新后的文件状态以及工作副本的一切变化
export则是普通的导出操作,不会生成.svn隐藏文件夹
注意:checkout命令缩写:co
```
### 使用频率较高的SVN客户端命令
| 命令 | 作用 |
| ---------- | ----------------- |
| svn add | 添加到版本控制 |
| svn commit | 提交修改到服务(创建一个新版本号) |
| svn update | 更新工作副本 |
| svn delete | 从版本库中删除文件或目录 |
#### svn add操作详情
```
svn add index.html // 增加一个文件:index.html
svn add css // 增加一个目录:css
svn add js -- non-recursive // 只增加一个目录:js,不会增加js目录下的各种js文件
svn add * // 将所有的文件全部增加上去,已经增加的就不加了,已经存在的目录,就不会添加其中的文件
svn add * --force // 强制执行添加
```
#### svn commit操作详情
```
commit缩写:ci (注意不要和checkout的缩写co混淆)
svn ci -m "this is index" index.html
// 提交文件,m是提交备注,必须写,哪怕这次只是小更新也要写,如果实在没有东西说,就" ",强制的(注:成功后会提示Committed revision 1,版本号更新)
svn ci -m "this is index" index // 提交目录
svn ci -m "this is index" * // 提交所有改动的文件
```
#### svn update操作详情
```
在服务端操作,把之前客户端的add以及commit操作同步更新
svn up // 全目录更新(update的缩写:up)
svn up -r 1 index.html // 想看一下index.html在版本1的时候是什么样的,更新(成功后会提示At revision 1)
svn up // 默认情况下,每个文件只会从服务端更新一次最新版本(除非有人把之前更新过其他版本的index.html修改过,或者利用Linux里的rm命令将index.html删掉以后使用svn up恢复最新版本,但是不如利用svn up)
svn up * // 强制更新所有文件到最新版本
```
#### svn delete操作详情
```shell
注意:Linux里的删除和SVN里的删除是两回事,Linux里的删除只是删除文件,但是可以通过SVN服务端的svn up命令恢复;SVN删除时把服务端里的文件删除,不能恢复
svn delete/del/remove/rm index.html -m "It's shit"
// SVN删除命令,四个都可以,等价,而m也是注释,和commit不同的是,这个m不强制要求写,写不写都可以,最好是写(成功后提示:D index.html)
svn ci -m "" // 需要提交,再去客户端去svn up会提示:D index.html Update to revision 3
```
### 使用频率相对较高的SVN命令
| 命令 | 作用 |
| --------- | --------------- |
| svn diff | 版本差异比较 |
| svn mkdir | 创建目录并增加版本控制 |
| svn cat | 不检出工作副本直接查看指定文件 |
#### svn diff操作详情
```
第一种用法:
首先假装将一个空的index.html通过vim添加12行代码
svn di index.html(di是diff的缩写)
输出-代表没被修改的内容,+代表被修改的内容,因为之前是空的index.html,所以全部都是+
@@ -0,0 +1,12 @@
-0,0表示存在差异的行号
+1,12表示本地工作副本中的index.html文件存在差异的是第1——12行的内容
使用diff,会全部显示-和+的内容,做对比
第二种用法:
svn di -r 2 index.html // 指定index.html工作副本和版本2的区别
svn di -r 1:3 index.html // 指定index.html的版本1和版本3的区别
svn di // 将当前目录下所有存在差异的文件全部罗列出来(不推荐)
```
#### svn mkdir操作详情
```
svn mkdir doc // 创建一个名为doc的文件夹并直接添加到版本控制中
注意:和Linux中的mkdir命令不同,Linux中mkdir创建以后需要使用add命令将其添加到版本控制中,而svn mkdir doc是直接就放上去了,两步合一步
```
#### svn cat操作详情
```
注:可以不在svn隐藏目录下执行操作
svn cat svn://10.0.146.110/index.html // 不检出index.html直接在线查看index.html的文件内容
```
#### 工作副本还原(SVN版Ctrl+Z)
```
svn revert index.html // 单个恢复index.html至服务端的最新版本
svn revert * // 自动扫描当前目录下的所有修改过的文件并恢复至服务端的最新版本
svn revert --recursive *
// 自动扫描当前目录以及所有子目录修改过的文件并恢复至服务端的最新版本
```
#### 二进制冲突与树冲突
* 冲突通常是在很长时间没有更新过工作副本时容易发生
* 产生原因:工作副本的修改与你执行svn update更新到的数据正好在同一个地方 ,如图
上图解决冲突方法:在9:25的时候提前svn update再执行修改操作
总结:避免冲突的方法
##经常更新工作副本
以上冲突都是php、html、css、js、java等修改造成冲突,又被称作二进制冲突
### 树冲突
* 发生树冲突的文件都不是二进制文件(比如图片)
* 树冲突无法精确到行并且处理树冲突必须处理整个文件
### 冲突处理
```
当提交你修改的文件提示:File XXX is out of data(您提交的文件已经过时),90%可能就是发生冲突了
此时先进行版本更新:svn up
其中三个选项:mc(保留我的版本),tc(保留对方版本),df(显示差异)
不确定的话df对比下然后和冲突人商量
df对比后,<<<<<<<<<到==========之间是自己的(mine)
==============到>>>>>>>>>>>之间是冲突人的
如果此时无法做出决定,可以选择p推迟提交(推荐)
如果此时决定了,可以选择e编辑该文件
编辑模式下底部有很多命令,很详细,这里不具体写出来
冲突处理的两种方式:
1、vim index.html 然后删除无关内容,然后保存退出
2、svn resolve index.html // 利用命令修改
利用命令修改后会出现一系列菜单,选择mc(保留我的)或tc(保留对方的)即可
处理冲突后要告诉服务端冲突解决了,否则后期无法提交,因为服务端会一直认为你有个冲突未解决,解决方式:
svn resolved index.html // 告诉服务器你已经处理了冲突(注:是resolved,完成时,这里要注意一下)
小知识:通过vim解决冲突,那么svn resolved index.html是必须的,但是如果用svn resolve index.html解决冲突,那么可以省略svn resolved index.html步骤
最后尝试一下能否提交成功,成功了表示解决了
```
### 锁定lock与解锁unlock
| 命令 | 作用 |
| ---------- | ------------------ |
| svn lock | 锁定文件,防止其他成员对文件进行提交 |
| svn unlock | 解锁文件 |
#### lock与unlock操作详情
```
在svn1运行:
svn lock index.html
在svn2运行:
vim index.html
svn ci -m "this is index" index.html
此时会提示该文件被锁了
在svn1运行:
svn unlock index.html
在svn2运行:
vim index.html
svn ci -m "this is index" index.html
此时会提示提交成功
扩展:
当svn1锁定index.html了,svn2修改后无法提交,svn1修改提交后会自动解锁,如果不想自动解锁可以运行如下指令:
svn ci -m "" --no-unlock index.html // 锁定index.html后svn1修改提交index.html后不自动解锁
珍爱生命,远离锁定,开发中虽然锁定可能避免冲突,但是尽量不用,因为你锁定了别人都用不了了,这样任务完不成会极大的降低团队的开发效率
```
### SVN进阶应用
| 命令 | 作用 |
| ---------- | ------------------------ |
| svn list | 列出当前目录下处于版本控制的所有文件以及顶级目录 |
| svn status | 列出工作副本中的文件(夹)状态 |
| svn log | 查看提交日志(来自svn ci的-m参数) |
| svn info | 工作副本及文件(夹)的详细信息 |
#### svn list操作详情
```
svn ls // 列出当前目录下处于版本控制的所有文件以及顶级目录(不显示子目录)
svn ls --recursive // 递归列出所有的当前目录下的处于版本控制的所有文件以及子目录里的文件
svn ls -v // 可以列出当前目录下所有文件的详情(包括文件最后一次被谁修改等详情)
svn ls -v --recursive // 可以递归列出当前目录下所有文件以及子目录里所有文件的详情
```
#### svn status操作详情
```
svn st // 展示文件状态,具体符号含义如下:
? // 无版本控制(还没有被添加到版本控制中)
D // 已被标记从版本库中删除(用的是svn rm命令删除)
M // 已经被编辑过
A // 已被标记增加到版本控制中
R // 文件被替换(svn rm删除以后又建立了一个同名文件)
C // 文件存在冲突,需要赶紧解决,否则无法提交
! // 文件缺失(用rm命令删除了文件,版本库中不会删除,此时用!表示文件缺失)
```
#### svn log操作详情
```
svn log // 可以清楚地查看所有记录包括版本号、提交人及提交时间还有修改的行数
snv log index.html // 只列出与index.html相关的提交日志
```
#### svn info操作详情
```
svn info // 可以看到版本库的url路径、最高的版本号、最后一次提交的用户、以及提交的时间等
svn info index.html // 指定查看index.html的相关信息
svn info --xml // 指定以xml格式输出相关信息
svn info --xml >> info.html // 保存相关信息并输出xml文件
```
### 多版本库解决方案
TCP/IP协议规定端口号范围为0-65535号
* 0—1023:公认端口,如http的80端口
* 1024—49151:注册端口,如SVN的3690端口;一个软件对应一个端口,去TCP/IP上注册,先到先得
* 49152—65535:私有端口,用户随意定义
以上端口号是比较大众化的,也可以内部自行修改,如公司内部把Mysql端口号改成一个只有内部知道的端口号,外界谁也连不上,能够提高安全性
#### 利用端口号实现一台服务器运行多个版本库的方法
```
假设此时的场景:有三个版本库 hello world Syears
svnserve -d -r /svnroot/hello // 运行hello版本库(假设之前Syears版本库已经在运行)
这里要注意:会提示Address already in use,原因是之前Syears版本库在运行,占用了默认端口3690
svnserve -d -r /svnroot/hello --lesten-port 3691 // 成功运行hello版本库(端口号为3691)
svnserve -d -r /svnroot/world --lesten-port 3692 // 成功运行world版本库(端口号为3692)
创建以后记得修改配置文件,在上面有笔记,需要注意的是,必须在创建版本库之前、运行版本库之后修改其配置文件,否则不会生效,如果运行了以后才发现忘记配置,可以使用如下命令:
killall svnserve // 关闭所有正在运行的版本库
mv svn_1/ Syears3690 // 修改名字方便区分,这里3690是SVN默认端口,写不写均可
svn co svn://10.0.146.110:3691 hello3691
// 检出hello版本库的工作副本,hello3691是自己设定的,不写的话系统会默认一个名称
svn co svn://10.0.146.110:3692 world3692
// 检出world版本库的工作副本
利用端口号运行多个版本库的方法缺点:如果一台服务器上有几十个甚至上百个版本库,那么端口号很难记住
```
#### 只利用一个端口号实现运行多个版本库的方法
```
前提:版本库依旧使用上述三个版本库:hello,world,Syears,且他们必须在同一目录下
svnserve -d -r /svnroot/ // 直接运行svnroot下的所有版本库,默认端口3690
此处与利用多个端口号实现的方法不同的是,多个端口号可以将不同目录下的版本库同时启动但是多了端口号记不住;而只利用一个端口号实现运行多个版本库的方法硬性要求版本库都在同一级目录下
svn co svn://10.0.146.110/Syears // 检出名为Syears版本库的工作副本
svn co svn://10.0.146.110/hello // 检出名为hello版本库的工作副本
svn co svn://10.0.146.110/world // 检出名为world版本库的工作副本
此时ls一下,各个版本库对应文件夹都有,就可以操作了
```
### 多版本库解决方案优缺点总结
| | 优点 | 缺点 |
| :---: | ------------- | ------- |
| 多个端口号 | 版本库可以创建在任意位置 | 端口号容易混淆 |
| 一个端口号 | 版本库必须创建在同一目录下 | 无需分配端口号 |
### svn copy操作详情
#### 工作副本=>工作副本复制
```
需求:新建的html文件和index.html文件内容大致相同
svn cp index.html copy.html
// 复制index.html为一个名为copy.html的新文件,该命令操作以后直接提交至版本库,无需再次提交
注意:如果是Linux系统操作命令的copy,则操作以后是无版本控制状态的,需要重新提交
需求:新建的html文件和index.html在版本4的状态下的文件内容差不多
svn cp -r 4 index.html copy4.html // 复制版本4的index.html文件为一个名为copy4.html的新文件
支持批量复制
svn mkdir temp // 新建文件夹
svn cp index.html about.html ./temp // 支持批量复制,但是文件名不能相同,所以需要另建目录
```
#### 工作副本=>版本库复制
```
svn cp index.html svn://10.0.146.110/Syears/target.html -m "copy a file"
// 复制index.html到Syears版本库中并命名为target.html,这里是直接提交了所以必须加m备注,本地工作副本没有target.html文件,版本库里有,所以...
svn update // 将版本库中的target.html同步到本地工作副本
这里同样支持-r参数,同上
注意:不支持跨库操作,Syears检出的工作副本只能复制到Syears版本库中
```
#### 版本库=>工作副本复制
```
svn cp svn://10.0.146.110/Syears/index.html demo.html
// 把线上版本库上的最新版本的index.html复制到本地工作副本并且取名为demo.html
注意:支持跨库操作(和上面的区分)
```
### 主干版本和分支版本
```
svn cp svn://10.0.146.110/Syears/ svn://10.0.146.110/Syears/trunk -m "set a new trunk"
版本库=>版本库的操作,这里注意,不支持跨库操作
svn cp svn://10.0.146.110/Syears/trunk svn://10.0.146.110/Syears/branch -m "set a new branch"
此时版本库目录下有了两个目录,一个是主干版本,一个是分支版本,两者互不干扰
工作习惯,建立一个版本库先建立一个主干版本和一个分支版本
```
## SVN高级应用
### HOOKS钩子应用
* 钩子就是当执行某些特定操作时触发执行预先设定好的任务
举例:贴吧签到、发帖回帖给经验等等
```
cd /svnroot/Syears/
cd hooks/
ls
tmpl后缀文件为钩子模板文件,执行的话复制一下然后去掉tmpl后缀然后编辑其即可,操作如下
cp -a post-commit.tmpl post-commit // 复制操作
chmod +x post-commit // 加一个权限,否则默认无法执行
变绿为可执行
vim post-commit
删除最后三行实例代码
需求:提交新版本后,能把最新版本的详细信息保存为xml文件
代码实例:
svn info svn://10.0.146.110/Syears --xml >> /var/www/repo.xml
wq!
重启SVN服务:
killall svnserve // 停止运行SVN服务
svnserve -d -r /svnroot/ // 启动SVN服务
cd /Syears/
cd trunk
vim index.html(这里随便改,有改动即可,要的是修改这个操作)
svn ci -m ""
此时,在浏览器中输入10.0.146.110,应该会有一个repo.xml文件,因为提交操作会生成xml文件,脚本里写着
注意:钩子应用是很强大的,无限大,只要脚本改的好
```
### 版本库的精简与丢弃
需求分析:
* 版本库使用越久,版本库号越多。需要删除之前较早的版本,来瘦身
```
演示操作前提:多提交几次,将版本库号搞大然后删除1—5版本号的版本
killall svnserve // 先停止SVN服务,防止瘦身过程中有人提交代码丢失信息
svnadmin dump /svnroot/Syears/ -r 6:16 > /Syears.repo
// 备份6—16版本号的版本库到根目录的Syears.repo中
svnadmin create /svnroot/newSyears // 创建新的版本库
svnadmin load /svnroot/newSyears/ < /Syears.repo
注意:新版本库依旧是从1开始的,然后依次往下数,不可能出现断层现象(如数据库自增id删除后还是继续增加)
cp -av /svnroot/Syears/conf/* /svnroot/newSyears/conf/
将旧版本库的配置文件全部搞到新版本库的配置文件中
rm -rvf /svnroot/Syears/ // 删除之前的版本库
svn -d -r /svnroot/ // 重启版本库
注意:精简版本库后,因为版本库号都发生了变化,需要重新检出工作副本,否则会报版本不存在错误信息
```
### 版本库的迁移与重定向
需求:更换服务器了,就要把原来服务器上的SVN数据迁移到新的服务器上,两种方法:
* 第一种方法:版本库的备份,就是精简操作步骤下来以后把备份的Syears.repo搞到新服务器上即可
* 第二种方法:和第一步原理一样,只不过是先将版本库压缩了然后再复制到新服务器上解压
两种方式区别:
* 第一种方式好比是将一棵树上的鸟窝放到另一棵树上,虽然鸟窝没变但是树变了,这里树是版本库,鸟窝是数据
* 第二种方式好比是把树连根拔起换个地方,树和鸟窝都是之前的
重定向:换服务器了,url也换了,这就叫重定向
```
killadd svnserve // 停止所有SVN服务
cd /svnroot/
ls // 里面有三个之前用过的:Syears hello world
mv world newworld
// 修改world为newworld,为了演示一下,假装newworld版本库是从别的服务器上备份来的
svnserve -d -r /svnroot/ // 启动SVN服务
cd /world/
svn up // 会提示10.0.146.110/world/不存在,因为已经被迁移了
svn switch/sw --relocate svn://10.0.146.110/world svn://10.0.146.110/newworld
// switch缩写是sw,这一步可以完成重定向操作
svn up // 可以正常运行
```
## SVN常见的坑及课程总结
* 忘记配置svnserve.conf
// 虽然可以运行SVN服务端,但是没法登陆
* 运行版本库的路径问题
// 版本库的目录名称运行时没写,检出时必须写;运行的时候写了,检出时不能写
// 否则会报错:版本库不存在
* svnadmin dump/load 管道符号问题
// 备份dump是> load(加载)是< 千万注意!否则会造成不可逆的数据丢失问题
##### Window系统中SVN学习地址:http://www.imooc.com/learn/109
| 特性 | CVS | SVN | GIT |
| ---- | ----- | ---- | ---- |
| 并发修改 | 支持 | 支持 | 支持 |
| 并发提交 | 不支持 | 支持 | 支持 |
| 历史轨迹 | 不支持更名 | 支持更名 | 支持更名 |
| 分布式 | 不支持 | 不支持 | 支持 |
```
并发修改:多个开发人员同时对同一个文件修改,最重要,最常用
```
```
并发提交:CVS只能一个一个提交,分别记录版本号;而SVN和GIT支持同时提交,整合在一个版本号里
这里CVS占内存但是方便找bug,而SVN和GIT正好相反
```
```
分布式:指的是是否需要一台专门的服务器来运行版本控制器。CVS和SVN需要专门的服务器,而GIT是分布式,不需要专门的服务器来运行版本控制器,各开发人员的电脑组成的网络就可以运行git,适合源代码的发布和交流
```
## SVN服务端命令
```
svnserve——控制svn系统服务的启动等
svnadmin——版本库的创建/导出/导入/删除等
svnlook——查看版本库的信息等
```
## 客户端命令
```
svn——版本库的检出/更新/提交/重定向等
```
## 版本库的创建与删除
### 创建版本库
```
svnadmin create/path/repos // path是版本库路径,repos是版本库名称
```
### 删除版本库
```
rm -rvf/path/repos // path是版本库路径,repos是版本库名称
```
### 配置文件位于 /path/repos/conf/
```
authz // 配置用户组及用户组权限
passwd // 配置用户名和密码
svnserve.conf // 配置默认权限、权限配置文件及密码配置文件
svnserve.conf配置文件如下:
vim svnserve.conf i
// 权限分为三种:none(没有权限)、read(允许更新代码)、write(允许提交代码)
将以下两行代码解掉#注释
anon-access = read // 没验证的用户允许更新代码
auth-access = write // 没验证的用户允许提交代码
注:可以根据需求修改权限
password-db = passwd // 指定用户名和用户密码配置文件的路径
authz-db = authz // 指定权限分组配置的路径
注:以上两个路径默认就好,也可以修改
passwd配置文件如下
vim passwd i
可以添加用户
Syears = 123456 // 用户名为Syears、密码为123456
注:可以添加多个
authz配置文件如下
vim authz i
[group] // 组
pm = Syears // 组名 = 用户名(项目经理组)
dev = Syears1,Syears2 // 组名 = 用户列表(用户与用户之间用,隔开)(开发组)
rcookie = Syears3 // (菜鸟组)
[/] // 版本库的根目录
@pm = rw // 对应pm的权限:可提交可修改(项目经理)
@dev = r // 对应dev的权限:只能修改(开发组)
@rookie = r // 对应rookie的权限:只能修改(菜鸟组)
[Syears:/] // 权限只适用于名字为Syears的版本库
@pm = rw
Syears1 = rw // 给指定的用户添加权限
Syears1 = r
Syears1 =
[repos:/xxx]:指定版本库下的某个目录的权限
* = r // 所有用户的权限均为r
注意:创建以后记得修改配置文件,在上面有笔记,需要注意的是,必须在创建版本库之前、运行版本库之后修改其配置文件,否则不会生效,如果运行了以后才发现忘记配置,可以使用如下命令:
killall svnserve // 关闭所有正在运行的版本库
```
# 注意
```
update是指将SVN服务端上的代码更新到本地
而本地代码上传到SVN服务端是commit
```
### 运行SVN服务端
```
svnserve -d -r /svnroot/Syears
```
### SVN服务开机自启
```
vim /etc/rc.local i
在exit0前添加:svnserve -d -r /svnroot/imooc/
```
### checkout和export的区别
```
checkout检出会生成.svn隐藏文件夹,他记录着工作副本最后一次更新后的文件状态以及工作副本的一切变化
export则是普通的导出操作,不会生成.svn隐藏文件夹
注意:checkout命令缩写:co
```
### 使用频率较高的SVN客户端命令
| 命令 | 作用 |
| ---------- | ----------------- |
| svn add | 添加到版本控制 |
| svn commit | 提交修改到服务(创建一个新版本号) |
| svn update | 更新工作副本 |
| svn delete | 从版本库中删除文件或目录 |
#### svn add操作详情
```
svn add index.html // 增加一个文件:index.html
svn add css // 增加一个目录:css
svn add js -- non-recursive // 只增加一个目录:js,不会增加js目录下的各种js文件
svn add * // 将所有的文件全部增加上去,已经增加的就不加了,已经存在的目录,就不会添加其中的文件
svn add * --force // 强制执行添加
```
#### svn commit操作详情
```
commit缩写:ci (注意不要和checkout的缩写co混淆)
svn ci -m "this is index" index.html
// 提交文件,m是提交备注,必须写,哪怕这次只是小更新也要写,如果实在没有东西说,就" ",强制的(注:成功后会提示Committed revision 1,版本号更新)
svn ci -m "this is index" index // 提交目录
svn ci -m "this is index" * // 提交所有改动的文件
```
#### svn update操作详情
```
在服务端操作,把之前客户端的add以及commit操作同步更新
svn up // 全目录更新(update的缩写:up)
svn up -r 1 index.html // 想看一下index.html在版本1的时候是什么样的,更新(成功后会提示At revision 1)
svn up // 默认情况下,每个文件只会从服务端更新一次最新版本(除非有人把之前更新过其他版本的index.html修改过,或者利用Linux里的rm命令将index.html删掉以后使用svn up恢复最新版本,但是不如利用svn up)
svn up * // 强制更新所有文件到最新版本
```
#### svn delete操作详情
```shell
注意:Linux里的删除和SVN里的删除是两回事,Linux里的删除只是删除文件,但是可以通过SVN服务端的svn up命令恢复;SVN删除时把服务端里的文件删除,不能恢复
svn delete/del/remove/rm index.html -m "It's shit"
// SVN删除命令,四个都可以,等价,而m也是注释,和commit不同的是,这个m不强制要求写,写不写都可以,最好是写(成功后提示:D index.html)
svn ci -m "" // 需要提交,再去客户端去svn up会提示:D index.html Update to revision 3
```
### 使用频率相对较高的SVN命令
| 命令 | 作用 |
| --------- | --------------- |
| svn diff | 版本差异比较 |
| svn mkdir | 创建目录并增加版本控制 |
| svn cat | 不检出工作副本直接查看指定文件 |
#### svn diff操作详情
```
第一种用法:
首先假装将一个空的index.html通过vim添加12行代码
svn di index.html(di是diff的缩写)
输出-代表没被修改的内容,+代表被修改的内容,因为之前是空的index.html,所以全部都是+
@@ -0,0 +1,12 @@
-0,0表示存在差异的行号
+1,12表示本地工作副本中的index.html文件存在差异的是第1——12行的内容
使用diff,会全部显示-和+的内容,做对比
第二种用法:
svn di -r 2 index.html // 指定index.html工作副本和版本2的区别
svn di -r 1:3 index.html // 指定index.html的版本1和版本3的区别
svn di // 将当前目录下所有存在差异的文件全部罗列出来(不推荐)
```
#### svn mkdir操作详情
```
svn mkdir doc // 创建一个名为doc的文件夹并直接添加到版本控制中
注意:和Linux中的mkdir命令不同,Linux中mkdir创建以后需要使用add命令将其添加到版本控制中,而svn mkdir doc是直接就放上去了,两步合一步
```
#### svn cat操作详情
```
注:可以不在svn隐藏目录下执行操作
svn cat svn://10.0.146.110/index.html // 不检出index.html直接在线查看index.html的文件内容
```
#### 工作副本还原(SVN版Ctrl+Z)
```
svn revert index.html // 单个恢复index.html至服务端的最新版本
svn revert * // 自动扫描当前目录下的所有修改过的文件并恢复至服务端的最新版本
svn revert --recursive *
// 自动扫描当前目录以及所有子目录修改过的文件并恢复至服务端的最新版本
```
#### 二进制冲突与树冲突
* 冲突通常是在很长时间没有更新过工作副本时容易发生
* 产生原因:工作副本的修改与你执行svn update更新到的数据正好在同一个地方 ,如图
上图解决冲突方法:在9:25的时候提前svn update再执行修改操作
总结:避免冲突的方法
##经常更新工作副本
以上冲突都是php、html、css、js、java等修改造成冲突,又被称作二进制冲突
### 树冲突
* 发生树冲突的文件都不是二进制文件(比如图片)
* 树冲突无法精确到行并且处理树冲突必须处理整个文件
### 冲突处理
```
当提交你修改的文件提示:File XXX is out of data(您提交的文件已经过时),90%可能就是发生冲突了
此时先进行版本更新:svn up
其中三个选项:mc(保留我的版本),tc(保留对方版本),df(显示差异)
不确定的话df对比下然后和冲突人商量
df对比后,<<<<<<<<<到==========之间是自己的(mine)
==============到>>>>>>>>>>>之间是冲突人的
如果此时无法做出决定,可以选择p推迟提交(推荐)
如果此时决定了,可以选择e编辑该文件
编辑模式下底部有很多命令,很详细,这里不具体写出来
冲突处理的两种方式:
1、vim index.html 然后删除无关内容,然后保存退出
2、svn resolve index.html // 利用命令修改
利用命令修改后会出现一系列菜单,选择mc(保留我的)或tc(保留对方的)即可
处理冲突后要告诉服务端冲突解决了,否则后期无法提交,因为服务端会一直认为你有个冲突未解决,解决方式:
svn resolved index.html // 告诉服务器你已经处理了冲突(注:是resolved,完成时,这里要注意一下)
小知识:通过vim解决冲突,那么svn resolved index.html是必须的,但是如果用svn resolve index.html解决冲突,那么可以省略svn resolved index.html步骤
最后尝试一下能否提交成功,成功了表示解决了
```
### 锁定lock与解锁unlock
| 命令 | 作用 |
| ---------- | ------------------ |
| svn lock | 锁定文件,防止其他成员对文件进行提交 |
| svn unlock | 解锁文件 |
#### lock与unlock操作详情
```
在svn1运行:
svn lock index.html
在svn2运行:
vim index.html
svn ci -m "this is index" index.html
此时会提示该文件被锁了
在svn1运行:
svn unlock index.html
在svn2运行:
vim index.html
svn ci -m "this is index" index.html
此时会提示提交成功
扩展:
当svn1锁定index.html了,svn2修改后无法提交,svn1修改提交后会自动解锁,如果不想自动解锁可以运行如下指令:
svn ci -m "" --no-unlock index.html // 锁定index.html后svn1修改提交index.html后不自动解锁
珍爱生命,远离锁定,开发中虽然锁定可能避免冲突,但是尽量不用,因为你锁定了别人都用不了了,这样任务完不成会极大的降低团队的开发效率
```
### SVN进阶应用
| 命令 | 作用 |
| ---------- | ------------------------ |
| svn list | 列出当前目录下处于版本控制的所有文件以及顶级目录 |
| svn status | 列出工作副本中的文件(夹)状态 |
| svn log | 查看提交日志(来自svn ci的-m参数) |
| svn info | 工作副本及文件(夹)的详细信息 |
#### svn list操作详情
```
svn ls // 列出当前目录下处于版本控制的所有文件以及顶级目录(不显示子目录)
svn ls --recursive // 递归列出所有的当前目录下的处于版本控制的所有文件以及子目录里的文件
svn ls -v // 可以列出当前目录下所有文件的详情(包括文件最后一次被谁修改等详情)
svn ls -v --recursive // 可以递归列出当前目录下所有文件以及子目录里所有文件的详情
```
#### svn status操作详情
```
svn st // 展示文件状态,具体符号含义如下:
? // 无版本控制(还没有被添加到版本控制中)
D // 已被标记从版本库中删除(用的是svn rm命令删除)
M // 已经被编辑过
A // 已被标记增加到版本控制中
R // 文件被替换(svn rm删除以后又建立了一个同名文件)
C // 文件存在冲突,需要赶紧解决,否则无法提交
! // 文件缺失(用rm命令删除了文件,版本库中不会删除,此时用!表示文件缺失)
```
#### svn log操作详情
```
svn log // 可以清楚地查看所有记录包括版本号、提交人及提交时间还有修改的行数
snv log index.html // 只列出与index.html相关的提交日志
```
#### svn info操作详情
```
svn info // 可以看到版本库的url路径、最高的版本号、最后一次提交的用户、以及提交的时间等
svn info index.html // 指定查看index.html的相关信息
svn info --xml // 指定以xml格式输出相关信息
svn info --xml >> info.html // 保存相关信息并输出xml文件
```
### 多版本库解决方案
TCP/IP协议规定端口号范围为0-65535号
* 0—1023:公认端口,如http的80端口
* 1024—49151:注册端口,如SVN的3690端口;一个软件对应一个端口,去TCP/IP上注册,先到先得
* 49152—65535:私有端口,用户随意定义
以上端口号是比较大众化的,也可以内部自行修改,如公司内部把Mysql端口号改成一个只有内部知道的端口号,外界谁也连不上,能够提高安全性
#### 利用端口号实现一台服务器运行多个版本库的方法
```
假设此时的场景:有三个版本库 hello world Syears
svnserve -d -r /svnroot/hello // 运行hello版本库(假设之前Syears版本库已经在运行)
这里要注意:会提示Address already in use,原因是之前Syears版本库在运行,占用了默认端口3690
svnserve -d -r /svnroot/hello --lesten-port 3691 // 成功运行hello版本库(端口号为3691)
svnserve -d -r /svnroot/world --lesten-port 3692 // 成功运行world版本库(端口号为3692)
创建以后记得修改配置文件,在上面有笔记,需要注意的是,必须在创建版本库之前、运行版本库之后修改其配置文件,否则不会生效,如果运行了以后才发现忘记配置,可以使用如下命令:
killall svnserve // 关闭所有正在运行的版本库
mv svn_1/ Syears3690 // 修改名字方便区分,这里3690是SVN默认端口,写不写均可
svn co svn://10.0.146.110:3691 hello3691
// 检出hello版本库的工作副本,hello3691是自己设定的,不写的话系统会默认一个名称
svn co svn://10.0.146.110:3692 world3692
// 检出world版本库的工作副本
利用端口号运行多个版本库的方法缺点:如果一台服务器上有几十个甚至上百个版本库,那么端口号很难记住
```
#### 只利用一个端口号实现运行多个版本库的方法
```
前提:版本库依旧使用上述三个版本库:hello,world,Syears,且他们必须在同一目录下
svnserve -d -r /svnroot/ // 直接运行svnroot下的所有版本库,默认端口3690
此处与利用多个端口号实现的方法不同的是,多个端口号可以将不同目录下的版本库同时启动但是多了端口号记不住;而只利用一个端口号实现运行多个版本库的方法硬性要求版本库都在同一级目录下
svn co svn://10.0.146.110/Syears // 检出名为Syears版本库的工作副本
svn co svn://10.0.146.110/hello // 检出名为hello版本库的工作副本
svn co svn://10.0.146.110/world // 检出名为world版本库的工作副本
此时ls一下,各个版本库对应文件夹都有,就可以操作了
```
### 多版本库解决方案优缺点总结
| | 优点 | 缺点 |
| :---: | ------------- | ------- |
| 多个端口号 | 版本库可以创建在任意位置 | 端口号容易混淆 |
| 一个端口号 | 版本库必须创建在同一目录下 | 无需分配端口号 |
### svn copy操作详情
#### 工作副本=>工作副本复制
```
需求:新建的html文件和index.html文件内容大致相同
svn cp index.html copy.html
// 复制index.html为一个名为copy.html的新文件,该命令操作以后直接提交至版本库,无需再次提交
注意:如果是Linux系统操作命令的copy,则操作以后是无版本控制状态的,需要重新提交
需求:新建的html文件和index.html在版本4的状态下的文件内容差不多
svn cp -r 4 index.html copy4.html // 复制版本4的index.html文件为一个名为copy4.html的新文件
支持批量复制
svn mkdir temp // 新建文件夹
svn cp index.html about.html ./temp // 支持批量复制,但是文件名不能相同,所以需要另建目录
```
#### 工作副本=>版本库复制
```
svn cp index.html svn://10.0.146.110/Syears/target.html -m "copy a file"
// 复制index.html到Syears版本库中并命名为target.html,这里是直接提交了所以必须加m备注,本地工作副本没有target.html文件,版本库里有,所以...
svn update // 将版本库中的target.html同步到本地工作副本
这里同样支持-r参数,同上
注意:不支持跨库操作,Syears检出的工作副本只能复制到Syears版本库中
```
#### 版本库=>工作副本复制
```
svn cp svn://10.0.146.110/Syears/index.html demo.html
// 把线上版本库上的最新版本的index.html复制到本地工作副本并且取名为demo.html
注意:支持跨库操作(和上面的区分)
```
### 主干版本和分支版本
```
svn cp svn://10.0.146.110/Syears/ svn://10.0.146.110/Syears/trunk -m "set a new trunk"
版本库=>版本库的操作,这里注意,不支持跨库操作
svn cp svn://10.0.146.110/Syears/trunk svn://10.0.146.110/Syears/branch -m "set a new branch"
此时版本库目录下有了两个目录,一个是主干版本,一个是分支版本,两者互不干扰
工作习惯,建立一个版本库先建立一个主干版本和一个分支版本
```
## SVN高级应用
### HOOKS钩子应用
* 钩子就是当执行某些特定操作时触发执行预先设定好的任务
举例:贴吧签到、发帖回帖给经验等等
```
cd /svnroot/Syears/
cd hooks/
ls
tmpl后缀文件为钩子模板文件,执行的话复制一下然后去掉tmpl后缀然后编辑其即可,操作如下
cp -a post-commit.tmpl post-commit // 复制操作
chmod +x post-commit // 加一个权限,否则默认无法执行
变绿为可执行
vim post-commit
删除最后三行实例代码
需求:提交新版本后,能把最新版本的详细信息保存为xml文件
代码实例:
svn info svn://10.0.146.110/Syears --xml >> /var/www/repo.xml
wq!
重启SVN服务:
killall svnserve // 停止运行SVN服务
svnserve -d -r /svnroot/ // 启动SVN服务
cd /Syears/
cd trunk
vim index.html(这里随便改,有改动即可,要的是修改这个操作)
svn ci -m ""
此时,在浏览器中输入10.0.146.110,应该会有一个repo.xml文件,因为提交操作会生成xml文件,脚本里写着
注意:钩子应用是很强大的,无限大,只要脚本改的好
```
### 版本库的精简与丢弃
需求分析:
* 版本库使用越久,版本库号越多。需要删除之前较早的版本,来瘦身
```
演示操作前提:多提交几次,将版本库号搞大然后删除1—5版本号的版本
killall svnserve // 先停止SVN服务,防止瘦身过程中有人提交代码丢失信息
svnadmin dump /svnroot/Syears/ -r 6:16 > /Syears.repo
// 备份6—16版本号的版本库到根目录的Syears.repo中
svnadmin create /svnroot/newSyears // 创建新的版本库
svnadmin load /svnroot/newSyears/ < /Syears.repo
注意:新版本库依旧是从1开始的,然后依次往下数,不可能出现断层现象(如数据库自增id删除后还是继续增加)
cp -av /svnroot/Syears/conf/* /svnroot/newSyears/conf/
将旧版本库的配置文件全部搞到新版本库的配置文件中
rm -rvf /svnroot/Syears/ // 删除之前的版本库
svn -d -r /svnroot/ // 重启版本库
注意:精简版本库后,因为版本库号都发生了变化,需要重新检出工作副本,否则会报版本不存在错误信息
```
### 版本库的迁移与重定向
需求:更换服务器了,就要把原来服务器上的SVN数据迁移到新的服务器上,两种方法:
* 第一种方法:版本库的备份,就是精简操作步骤下来以后把备份的Syears.repo搞到新服务器上即可
* 第二种方法:和第一步原理一样,只不过是先将版本库压缩了然后再复制到新服务器上解压
两种方式区别:
* 第一种方式好比是将一棵树上的鸟窝放到另一棵树上,虽然鸟窝没变但是树变了,这里树是版本库,鸟窝是数据
* 第二种方式好比是把树连根拔起换个地方,树和鸟窝都是之前的
重定向:换服务器了,url也换了,这就叫重定向
```
killadd svnserve // 停止所有SVN服务
cd /svnroot/
ls // 里面有三个之前用过的:Syears hello world
mv world newworld
// 修改world为newworld,为了演示一下,假装newworld版本库是从别的服务器上备份来的
svnserve -d -r /svnroot/ // 启动SVN服务
cd /world/
svn up // 会提示10.0.146.110/world/不存在,因为已经被迁移了
svn switch/sw --relocate svn://10.0.146.110/world svn://10.0.146.110/newworld
// switch缩写是sw,这一步可以完成重定向操作
svn up // 可以正常运行
```
## SVN常见的坑及课程总结
* 忘记配置svnserve.conf
// 虽然可以运行SVN服务端,但是没法登陆
* 运行版本库的路径问题
// 版本库的目录名称运行时没写,检出时必须写;运行的时候写了,检出时不能写
// 否则会报错:版本库不存在
* svnadmin dump/load 管道符号问题
// 备份dump是> load(加载)是< 千万注意!否则会造成不可逆的数据丢失问题