SVN 学习笔记

Posted by gsfish on August 17, 2019

最近由于项目的原因开始研究 SVN,虽说这是一款年代久远的工具,在经过了解后发现,即使在当今它还是有很多亮点的。

0x00 关于 SVN

SVN 的全称为 Subversion,是一个开放源代码的版本控制系统,SVN 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。

SVN 的架构:

0x01 基本概念

版本控制模型

SVN 以复制-修改-合并模型为主,但是对某些类型的文件仍然需要使用加锁-修改-解锁模型。复制-修改-合并模型要求文件是支持合并的—也就是说文件是基于行的文本文件(例如程序源代码文件),但是对二进制文件(例如图片和音频文件)来说,合并有冲突的修改几乎是不可能完成的。在这种情况下,串行地修改文件就显得非常有必要。如果没有串行访问,用户花费大量时间作出的修改很可能会被丢弃。

加锁-修改-解锁模型:

复制-修改-合并模型:

仓库

仓库是存放 SVN 的版本控制数据的中央位置,用户及其软件通过工作副本与仓库交互。SVN 实现仓库的方式与其他版本控制系统非常类似。与工作副本不同,一个 SVN 仓库是一个抽象的实体,可以被 SVN 的库和工具进行独占性地操作。

工作副本

工作副本(working copy)是仓库的特定版本数据在本地的副本,用户可以自由地对它进行操作。工作副本中除了存放被版本控制的文件外,还有用于跟踪文件和与服务器通信的元数据。对其他软件来说,工作副本只是一个普通的本地目录,所以即使它们不具备版本控制功能也可以对工作副本进行读写。SVN 的客户端工具负责管理工作副本,以及与仓库通信。

版本号

SVN 客户端将任意多的文件和目录的修改作为一个原子事务提交给仓库。原子事务的意思是要么所有的修改都被仓库接受,要么一个也没有。SVN 尽量保证即使是在程序崩溃,操作系统崩溃,网络断开和有其他 用户干扰的情况下,也能维持住原子性。

仓库每接受一次提交都会为文件系统树创建一个新状态,叫作一个版本号(revision)。每一个版本号都与一个独一无二的自然数相关联,后一个版本号都比前一个大一。新创建的仓库的初始版本号是 0,除了一个空的根目录外,什么也没有。

0x02 SVN 的工作副本

一个 SVN 工作副本是用户本地系统中的一个普通目录,用户可以按照自己的要求对存放在目录中的文件进行编辑,如果是源代码文件,用户也可以按照通常的方式对它们进行编译。工作副本是用户的私有工作空间:除非用户明确地要求 SVN,否则它不会让工作副本合并其他人的修改,也不会把用户的修改暴露给其他人。用户可以为同一个项目创建多个工作副本。

如果用户修改了工作副本中的文件,并且确认了修改是正确的,此时可以使用 SVN 提供的命令来”发布”修改(通过把修改保存到仓库中),于是项目中的其他人就可以看到你的修改。如果其他人也发布了他们的修改,SVN 也提供了命令把他们的修改合并到你的工作副本中(通过读取仓库)。

工作副本还会包含一些额外的文件,这些文件由 SVN 创建并维护,用于命令的正常运行。每一个工作副本中都有一个名为 .svn 的子目录,它是工作副本的管理目录。管理目录中的文件可以帮助 SVN 识别哪些文件含有未发布的修改,哪些文件是过时的。

工作副本的工作原理

SVN 为工作副本中的每一个文件记录两项信息:

  • 版本号:这被称为文件的工作版本号(working revision)
  • 时间戳:记录了本地文件最近一次被仓库更新是在什么时候

有了这些信息后,通过与仓库通信,SVN 就可以判断出 每一个工作文件处于以下 4 种状态中的哪一种:

  1. 当前未修改的:文件在工作副本中未被修改,并且在工作版本号之后还没有人提交过该文件的修改。对文件执行 svn commitsvn update 都不会产生任何效果.
  2. 当前已修改的:文件在工作副本中已被修改,并且在一次更新以来还没有人向仓库提交过该文件的修改。本地有未提交的修改,于是执行 svn commit 将会成功地把修改提交到仓库中,而 svn update 不会产生任何效果.
  3. 过时未修改的:文件在工作副本中未被修改,但是在上一次更新之后有人往仓库提交了该文件的修改。为了让文件和最新版本保持同步,应该执行更新操作。对文件执行 svn commit 不会产生任何效果,执行 svn update 将 把仓库中的最新修改合并到文件中.
  4. 过时且已修改的:文件在本地工作副本和仓库都被修改了。对文件执行 svn commit 会由于文件已过时而失败。首先应该更新文件,命令 svn update 试图 把仓库的修改合并到本地。如果 SVN 不能自动地以一种 合理的方式完成合并,就会把冲突交由用户来解决.

0x03 SVN 基本用法

往仓库中添加数据

命令 svn import 可以快速地向仓库中添加新文件或目录。svn import 不要求工作副本,新增的文件会马上提交到仓库中。使用该命令的典型情况是用户想要把一个已存在的目录添加到 SVN 仓库中,例如:

svn import SRC DST [-m MSG]

上面的例子把本地目录 mytree 中的内容添加到仓库的 some/project 目录中。注意,用户在导入前无需创建新目录——svn import 会自动完成这些工作。提交后,用户就可以在仓库中看到新增的文件和目录:

svn list SRC

创建工作副本

检出(checkout)仓库中的目录将会在用户的本地主机上创建一个该目录的工作副本。除非特意指定,否则这个副本将包含仓库最新版本的数据:

svn checkout SRC

更新工作副本

如果某个项目正在被多个工作副本修改,用户就需要更新自己本地的工作副本,以获取其他人提交的修改:

svn update

修改工作副本

工作副本支持的修改类型分为两种:文件修改(file changes)和目录修改(tree changes)。文件修改不需要告知 SVN,用户可以使用任意一种自己喜欢的工具来修改文件,SVN 可以自动检测到哪些文件发生了变化。目录修改涉及到目录结构的变化,例如添加和删除文件、重命名文件和目录、复制文件和目录。目录修改要使用 SVN 的命令完成。文件修改和目录修改只有在提交后才会更新到仓库中:

svn add FOO
svn delete FOO
svn copy FOO BAR
svn move FOO BAR
svn mkdir FOO

审核修改

在提交之前,应该查看一下自己到底修改了哪些东西。

查看修改的整体概述:

svn status [FOO]

查看修改的细节:

svn diff [FOO]

修正错误

SVN 提供了一种简便的方法来撤消工作副本中的修改,把文件或目录恢复到修改前的样子:

svn revert [-r REV] [FOO]

解决冲突

当一个用户正在修改文件时,其他人可能已经把自己的修改提交到了服务器上。为了防止在提交修改时,由于工作副本过旧导致提交失败,用户需要把其他人的修改更新到本地:

svn update

如果命令的执行结果有冲突产生:

svn resolve --accept [ACT] [FOO]

关于 ACT:

  • base:使用上一次检出时版本
  • mine-full:保留自己的修改
  • theirs-full:保留从服务器收到的更新

提交修改

把工作副本的修改提交到仓库中,如果修改 被接受,其他用户就可以看到这些修改:

svn commit [-m MSG]

检查历史

从行的级别上查看修改的内容:

svn diff [-r REV]

查看和版本号绑定的日志消息,及其日期、作者、以及受影响的文件路径:

svn log  [-r REV]

根据给定的版本号,输出文件在该版本下的内容:

svn cat [-r REV]

根据给定的版本号,查看该版本下的文件的每一行的最后一次修改信息:

svn annotate [-r REV]

根据给定的版本号,列出仓库在该版本下的文件与目录清单:

svn list [-r REV] [SRC]

0x04 SVN 与 Git 的区别

  1. SVN 仓库为集中式管理,而 Git 仓库为分布式管理
  2. SVN 的权限管理可精确到具体路径,而 Git 只能做到仓库级别
  3. SVN 可下载仓库下的某一子目录,而 Git 只能下载完整的仓库
  4. SVN 一个仓库可对应多个项目,而 Git 一个仓库对应一个项目
  5. SVN 的分支与标签基于文件目录,而 Git 则基于引用
  6. SVN 对文件的完整性依赖于服务端给出的校验码,而 Git 则基于自身的机制(通过 SHA-1 计算的 Commit ID)

参考资料

  1. http://svnbook.red-bean.com/nightly/zh/svn-book.html#svn.advanced.props.special.ignore
知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。