SVN – 承前启后

虽然当前在互联网多人开发模式下,分布式的 Git 大行其道。但是,作为 Git 绝对的大哥,中央化的 SVN 仍有其不可忽视的地位和作用。很多公司内部的版本管理,仍然使用的是 SVN。从 VSS 到 SVN,再到 Git,不难看出版本管理软件发展前进的变化。在发展链中,后来者都向前者借鉴很多,又引入了一些新的理念和内容。SVN 作为版本管理软件发展过程中一个关键的纽带,其重要性不言而喻。

SVN 是 Apache Subversion 的缩写。最初由 CollabNet Inc 开发,后变成 Apache 的一个开源项目。与 VSS 一样,是一个集中式的代码版本管理工具。典型的 C/S 架构软件。

版本管理系统的设计

版本,通俗而言就是变更,版本管理系统,或称版本控制系统,本质上就是对变更进行管理的系统,进一步而言,就是对不断进行版本变化的文件进行管理的系统。这里涉及两个方面,一是管理文件,二是管理变更。从文件管理的角度而言,只要是文件,都可以纳入管理范畴,对文件的类型没有限制,如常见有以源代码为代表的文本文件、还包括各种二进制文件(如图片、声音、视频等)。从管理变更的角度而言,它必须提供一种机制,使得所有的变化都可以纳入它的监控和管理范围之内。由于这些特点,版本管理系统在一般的文件管理系统的基础上,拥有其独有的特点。

建立记录并跟踪数据变化的机制

要对文件的各个版本变化进行管理,首先就要为各个发生变化的文件建立“时光隧道”,通过这种“时光隧道”,能够记录和跟踪系统中各个文件数据的变化,并能够通过“时光隧道”,向前或向后获取和并处理各个时光节点的文件数据,而这也是版本管理系统的核心精要所在。

各个版本控制系统软件,首先,都必然会无一例外的建立起文件变更的“时光隧道”。同时,也因这种“时光隧道”建立具体机制不同,而有所不同。

  • 以“点”(文件)作为变更管理的基础

    这类版本控制系统,以文件为中心进行版本的管理,关注的是版本库中的每个点(每个文件),即以版本库中每个文件的变更情况作为单次版本变化的基础。VSS、CVS 都是这类系统。

  • 以“面”(整个库)作为变更管理的基础

    这类版本控制系统,以整个版本库(整个目录树)为中心进行版本的管理,关注的是版本库的“整体面(版本库的整个目录树)”,即以版本库整体(整个目录树)作为单次版本变更的基础。虽然从微观粒度上看,是文件的变更(文件内容的修改、文件的增加、删除、文件夹的增加、修改、删除)导致整个库的变更,但这类系统从版本库宏观视野进行管理,以整个目录树变化作为每次版本更迭的基础。版本管理系统中的每个版本号与整个版本库的一个状态建立映射、相对应,版本号不与具体文件的变化对应。SVN 就是这类系统的典型。

url-1

共享文件的内容同步机制

一个文件在版本变更的过程中,往往要经历多次、多人的编辑处理。在实际使用中,多人同时编辑一个文件的情况非常常见,此时,文件是作为共享资源被多个人使用,由于每个人对文件修改内容有差异,可能造成修改内容的冲突,版本控制系统必须能够提供文件内容的同步机制,保证所有人的修改都能最终整合并体现在文件中。

为此,有两种模型可以完成共享文件的内容同步。

1、封闭式模型 — “锁定-修改-解锁(lock-modify-unlock)模型”

lock-modify-unlock 模型是一种独占式、同步修改的使用模型。多个用户同时编辑版本库资源(如一个文件)时,系统提供相应的锁定机制,对版本库中共享文件的使用采用严格的轮转方式,在同一时刻只允许一个用户以独占的方式使用版本库中文件。得到使用权的用户锁定目标文件,进行修改,修改完后解锁文件,释放资源。在共享文件被锁定期间,其他用户不能修改版本库中的文件,只能在前序用户解锁文件后,再通过锁定来拿到资源使用权,完成版本库文件内容的更新。

这种方式类似操作系统对临界资源的使用方式。某种程度上可以说,库中文件版本与拿到使用权的用户修改的版本保持“同步”状态。对版本库的使用(出入库操作)类似一种“单线程”的操作方式,是一种严格的 “串行” 使用方式

url-2

2、开放式模型 — “拷贝-修改-合并(copy-modify-merge)模型”

copy-modify-merge 模型是一种共享、异步修改的使用模型。多个用户编辑版本库资源(如一个文件)时,大家可同时(或任意时刻)得到版本库中文件的一个拷贝,以版本库中一个共同的版本(也可以不同的版本)作为出发点,同时开展工作,完成各自的内容修改,而不需要锁定文件。修改完成后,所有用户可以分别按自身需求、随时向版本库提交自己的修改。

这种对版本库的使用(出入库操作)类似一种“多线程”的操作方式,所有用户共享、而非独占使用版本库,是一种的 “并行” 使用方式。在使用上,真正实现“任意时刻开始,任意时刻提交”。

url-3

这种使用方式也为文件的提交带来了新的挑战。由于各个用户并发修改及提交结果,导致后续提交修改的用户,面对的版本库往往已非其自身最初开始修改编辑的版本库。此时,版本库已经发生了变更,可能已经包含的其他用户修改后的提交结果,直接提交修改可能会导致对其他用户修改的覆盖,无法使所有用户的修改都整合体现在最终版本中。因此,此时的提交,应基于版本库中的最新版本,而不能基于最初开始修改编辑的版本,应首先获取版本库中的最新版本,将本地用户的修改与库中最新版本整合后,再将整合后的结果提交到版本库中。如此,可确保对各用户的修改进行了统一整合。

在整合过程中,如库中版本与本地修改文件内容没有冲突,可直接进行合并操作;如果库中版本与本地修改内容有冲突,则需要首先解决冲突(多为手动修改解决),再将最终整合结果提交。


在入库环节,copy-modify-merge 模型和 lock-modify-unlock 模型本质上没有区别,所有将入库的本地版本都必须基于库中目前最新快照(版本)来完成。

url-4

支持 copy-modify-merge 模式的各种版本控制系统,必须提供对应的机制,为新的文件提交方式提供支撑。

如 SVN 的 update 机制,即完成了版本库中最新版本与本地修改版本的合并及冲突检验工作。

3、两类模型各有千秋

copy-modify-merge 模型和 lock-modify-unlock 模型各有特色,各有各的优势场景。

copy-modify-merge 模型的一个最重要的特点是文件的合并(merge),要使文件合并有意义,必须依托易理解的上下文。这就对版本库中的文件提出了要求,文件必须能够提供通用的、版本控制系统易理解的上下文,版本控制系统对文件的合并才能发挥作用。如此,以行为基础的文本文件无疑是最为符合要求的目标(行本身就是文件内一种明显的分隔符)。copy-modify-merge 模型在处理文本文件时,如源代码文件、markdown 文件等,能够最大程度发挥其 merge 的威力。

对于二进制文件,其上下文含义并不明显,若使用 copy-modify-merge 模型,无法保证合并后有意义的结果,使用 copy-modify-merge 模型优势并不明显。二进制文件,如视频或声音,用户轮流修改是最为合适的共享使用方式,lock-modify-unlock 模型可以很好适应这种串行工作的需求。

两类模型是共享文件的两种基本的内容更新机制。很多版本管理系统(如 VSS、SVN)都同时提供对这两种模型的支持。

Subversion 版本管理机制的实现

SVN 的运行设计

SVN 整体上是一种类似“服务器 — 客户机”的运行模式,在“服务器”端,是 SVN 的版本库,集中管理所有文件和相关变更;版本库的每个用户,都拥有一个独立的“工作目录”,每个“工作目录”都是版本库中“某个版本(未必最新版)”的本地拷贝,用户在这个本地拷贝的基础上进行修改、提交。每个用户的“工作目录”,相当于 SVN 的“客户端”。SVN 的“服务器”和“客户端”采用本地访问协议或 TCP/IP 协议完成数据交流。

基于上述结构设计,SVN 的宏观运行流程为(copy-modify-merge模式为例):

  • 首先,copy:
    从库中拿“一个版本”到本地,建立本地工作拷贝;

  • 然后,modify:
    对本地工作拷贝进行修改;

  • 最后,merge:
    拉下(pull)库中最新版,与本地修改后拷贝合并,最后上传(push)

SVN 必须提供相应的机制,一是在用户本地,必须明确 copy 的是版本库中的哪个版本,即“本地拷贝”与版本库中具体哪个版本相对应(本源版本),二是将所有对“本地拷贝”的修改(内容的变更、文件夹结构的变化)都记录下来。为此,SVN 的方法是:在每个“本地拷贝”文件夹中,加入了一个名为 .svn 的文件夹,在这个 .svn 文件夹中,记录本源版本和变更情况。可将 .svn 视为一种“状态记录文件夹”。

url-5

SVN 的指令集工具

SVN 的指令形式

SVN 源于 linux 系统,所以其指令设计天然带有 linux 命令的基因。指令形式上采用 linux 的长指令模式,即采用主命令、辅命令、操作参数、操作对象相结合的模式。

SVN 的常用指令

SVN 提供了一套完整的指令集合满足其运行设计思路。其常用指令如下所示:

url-6

1、版本库不存在时 — 建立版本库

  • svnadmin create

    使用 svnadmin create 建立版本库,并设置版本库的访问方式(本地、网络服务),认证和授权信息等。

    svnadmin create /usr/local/svn/repo
    

2、版本库存在时 — 使用版本库

(1) 直接针对版本库的操作

  • svn import — 导入库

    svn import 操作,可以在没有任何工作拷贝(working copy)情况下,直接将外部文件导入一个特定的版本库。

    svn import mytree file:///usr/local/svn/repo/project/ -m "first"
    // 将 mytree 目录直接导入版本库中
    
    Adding  mytree/foo.c
    Adding  mytree/bar.c
    Adding  mytree/subdir
    Adding  mytree/subdir/quux.h
    
    • svn import将未版本化文件导入版本库的最快方法
    • 使用中,可以根据需要创建中介目录(如 mytree),中介目录中的文件会直接提交到版本库。完成 import 文件后,这个中介目录不会转化为一个工作拷贝;为了开始工作,还是需要运行 svn co 额外导出一个 working copy
      (这一点和 svn add 需要在 working copy 中不同)
    • import 方式通常在希望让一组文件快速入库时使用
  • svn list (ls) — 查看库文件

    使用 svn list + 库路径 查看库中的文件,如果 pwd 为工作拷贝目录,则可直接使用 svn list 查看库中文件。

(2) 针对 working copy 的操作

  • svn checkout — 建立 working copy
    • SVN 设计中,最初使用 checkout 建立 working copy。这个新建的 working copy 是有其版本对应的,即某个库存的版本,其版本可称为 “本源版本”。如不使用 update,working copy 中永远是最初 checkout 出来的 “本源版本”
  • svn status (st) — 查看 working copy 中文件状态

  • svn commit — 提交修改

    • 提交仅仅是提交,commit 操作并不改变 working copy 的版本信息,除非进行 update 操作
  • svn add — 将外部文件纳入 working copy 的 svn 监控中

    如前述运行设计所言,working copy 使用 .svn 文件夹建立了一套“监控体系”,来监控 working copy 中文件的变化,并以此作为其他针对 working copy 的 svn 操作的基础。working copy 监控体系中的被监控所有成员(文件)最初均来源于版本库,当 working copy 中引入外部文件时,仅仅将文件(夹)拷贝进 working copy,其本身是不纳入 .svn 监控体系的,只有使用 add 指令,外部文件才算正式纳入 .svn 的监控体系中,后续 svn 操作才能对这些 “已纳入监控体系” 的文件发生效果

    svn add . --no-ignore --force
    // 递归将 working copy 下所有新增文件纳入 .svn 监控
    
  • svn log — 查看版本修改信息
    • 如果不指定具体版本参数,log 操作的基础是 working copy 的本源版本
  • svn update (up) — 用库中版本(最新版或指定版本)更新 working copy
    • update最重要的 SVN 操作,其行为类似 “下载” 操作 — 将库中的某个版本 “下载” 到本地。不加参数时,默认 “下载” 库中最新版本;加上 -r + 版本号 参数,可以获得库中任意版本。update 操作是 SVN 真正的 “时光机”,也是很多其他 SVN 操作的基础
    • update 操作本质改变了 working copy 的本源版本(working copy 的基线版本/映射版本),而本源版本正是很多 svn 操作(如 svn log)数据来源的基础
    • copy-modify-merge 模式下,update 操作是基于本地 working copy 既有修改基础上的 update,执行 update 操作永远不会覆盖掉本地 working copy 既有修改,可放心大胆使用
    • copy-modify-merge 模式下,update 不会导致本地修改丢失,但会以 “行” 为基础,用 update 的本源版本与本地修改进行自动 merge 或 conflict 识别
      (update 是 merge 和 conflict 的前导操作)
    • update 检测到冲突时,会给出完备的冲突场景

      冲突场景,由文件的变更引起,即某个(某些)文件基于共同的起点版本被多头修改,各个修改后的文件要归一化时产生的不确定场景

      基于冲突文件,SVN 会在 working copy 中自动增加三个额外的版本化的外部文件,

      文件名 作用
      filename 带冲突信息的文件
      filename.mine update 操作前,working copy 中最新修改的文件
      filename.rOLD update 操作前,固有本源版本中的文件
      filename.rNEW update 操作后,最新本源版本中的文件

      如下图所示,

      pic-7

      冲突场景不解决,是无法进行入库(commit)操作的

    • 解决冲突,就是在上述 4 种文件中选择一个,作为归一后最终可接受文件。4 种文件中的任何一个都可以作为最终文件,选择取决于具体需求。

    • 一般情况下,冲突发生,常见处理两种方式如下:

      • 保留用户的本地修改,使用 svn resolved 告知版本库冲突解决

        修改 filename 文件(直接根据 diff 信息修改,或者,用其他文件覆盖),用 svn resolved <filename> 通知修改完成

      • 放弃用户本地修改,利用 svn revert <filename> 放弃所有本地修改,回溯到库中已有版本,即使用 update 后的库中版本,即使用 filename.rNEW 文件的内容

  • svn resolved — 通知冲突已解决

    • 使用 svn resolved <filename>,SVN 会自动删除冲突检测中自动增加的 .mine、.rOLD、.rNEW 文件,使 filename 进入可以 commit 的状态
  • svn info — 版本库信息查看
    • 指令格式:
      • svn info folder_or_file_path
    • 显示给定地址或路径(url)的版本库信息
    • 信息显示的内容包括:
      • 版本库整体信息
      • 指令中规定的文件夹或文件的修订信息
    • 查看版本库的相关信息,尤其是,可以通过工作目录溯源找到版本库的存储位置
    • 可以在工作目录中的任何位置,使用该指令简化格式 svn info

SVN 仓库的运行方式

svn 库有本地使用和网络使用两种运行方式。在网络上运行时,需要开启相应的 svn 网络服务,这种网络服务既可以使用 svn 专有协议承载,也可以使用 web 服务承载。

reference

  1. 《Version Control with Subversion for Subversion 1.4》
  2. https://www.runoob.com/svn/svn-tutorial.html

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注