This text is a work in progress—highly subject to change—and may not accurately describe any released version of the Apache™ Subversion® software. Bookmarking or otherwise referring others to this page is probably not such a smart idea. Please visit http://www.svnbook.com/ for stable versions of this book.
“Subversion 的工作副本”一节 已经说过, Subversion 的工作 副本是一种暂存区, 暂存用户的私有修改, 当修改完成, 准备共享给其他用户时, 就把修改提交到仓库中. 于是, 用户的大部分时间都是在用客户端与工作副本打 交道, 即使是不处理工作副本的操作 (例如 svn log), 也 经常使用工作副本里的文件或目录作为操作的目标文件.
明确地说, 从工作副本里提交是修改文件的典型方式, 幸运的是这并不是唯一 的选择, 如果修改相对比较简单, 用户甚至可以在不检出工作副本的前提下提交 修改, 本节就是介绍与此有关的内容.
为了完成一些相对较小的修改, Subversion 的客户端命令行工具的很 多操作都可以在没有工作副本的前提下, 直接对仓库 URL 发起. 其中的部分 内容在本书的其他地方介绍, 但是为了方便读者, 我们在这里详尽地列出了 它们.
最明显的远程类提交操作应该是命令 svn import, 我们在 “导入文件和目录”一节 介绍如何快速地把 一个目录导入到仓库中时, 提到了这个命令.
当目标参数是 URL 时, 命令 svn mkdir 和 svn delete 也可以是远程操作, 这允许用户在没有工作副本的前 提下, 在仓库中添加新的目录或 (递归地) 删除文件. 每次执行这两个命令时, 客户端与服务器的通信过程类似于把工作副本里新增的目录或删除的文件提交 给服务器的过程. 如果认证没有问题, 并且没有发生冲突, 服务器就在一个单独 的版本号里完成添加或删除.
你可以用两个 URL 作为 svn copy 或 svn move 的参数—一个是源, 另一个是目标—直接向 仓库提交文件的复制或移动. 如果是在工作副本里执行, 这两个操作将会是耗时 最长的操作之一, 如果使用仓库的 URL 进行远程操作, 它们就可以在常数时间 内完成. 实际上, 在创建分支时, 人们经常使用 svn copy 远程操作, 这部分内容将在 “创建分支”一节 介绍.
和普通的 svn commit 一样, 上面介绍的几个远程
操作都接受用户输入一段日志, 描述本次操作做了什么, 输入日志的方式可以
用选项 --file
(-F
) 或
--message
(-m
), 如果这两个选项都没有指定,
客户端就会提示用户输入日志消息.
最后, 很多与版本号属性相关的操作都可以直接对仓库发起. 实际上, 这 里谈到的版本号属性比较独特, 因为它们不是存放在工作副本里, 所以它们 必须 在不与工作副本交互的情况下修改. 关于如何管理 Subversion 属性的更多信息, 见 “属性”一节.
客户端命令行工具的远程提交操作的一个缺点是用户每次提交只能执行一
个操作—或者说一种类型的操作. 比如说在一个工作副本内, 为了用一个
全新的目录替换掉旧目录, 先执行 svn delete, 再执行
svn mkdir—是一个很自然的操作. 当用户提交这两
个操作的执行结果时, 仓库将创建一个新的版本号, 该版本号完整地记录了这两
个操作. 但是客户端命令行的远程操作不能在单个版本号中完成这两步操作
—svn delete URL
会创建一个新的版本号并删除目录; svn mkdir URL
会在第二个版本号中完成目录的创建.
幸运的是, Subversion 另外提供了一个工具, 用于把多个远程操作放在一 个提交中完成, 这个工具是 svnmucc—Subversion 多 URL 命令行客户端 (Multiple URL Command Client):
$ svnmucc --help Subversion multiple URL command client usage: svnmucc ACTION... Perform one or more Subversion repository URL-based ACTIONs, committing the result as a (single) new revision. Actions: cp REV URL1 URL2 : copy URL1@REV to URL2 mkdir URL : create new directory URL mv URL1 URL2 : move URL1 to URL2 rm URL : delete URL put SRC-FILE URL : add or modify file URL with contents copied from SRC-FILE (use "-" to read from standard input) propset NAME VAL URL : set property NAME on URL to value VAL propsetf NAME VAL URL : set property NAME on URL to value from file VAL propdel NAME URL : delete property NAME from URL …
svnmucc 很多年前就已经包含在 Subversion 的源代 码树中 (那时候称为 mucc), 但是直到 1.8, svnmucc 才享受到完全的支持, 成为 Subversion 客户端命令行工 具套装的正式成员.
svn 可以做到的转换, svnmucc
都可以做到, 但不同的是, svnmucc 的功能并不是把操作
切分成多个子命令. 用户可以在一条命令行上 (或者在一个文件中, 通过选项
--extra-args
(-X
) 把文件传递给
svnmucc) 输入多个操作及其参数, svnmucc
支持的某些操作模仿了对应的客户端命令行. 读者可能已经注意到
svnmucc 帮助信息中
列出的操作, 例如 cp
, mkdir
,
mv
和 rm
, 和我们在
“远程客户端命令行操作”一节 提到的操作非常
类似, 但是请记住, 它们之间最关键的区别是用户可以在
svnmucc 的一次调用中, 执行任意多的操作, 所有的这些
操作只会产生一个新的版本号.
如果使用 svnmucc 完成本节开头的远程目录替换操 作, 一个示例是:
$ svnmucc rm http://svn.example.com/projects/sandbox \ mkdir http://svn.example.com/projects/sandbox \ -m "Replace my old sandbox with a fresh new one." r22 committed by harry at 2013-01-15T21:45:26.442865Z $
可以看到, svnmucc 在一个版本号中完成了两步操作, 而在没有工作副本的情况下, svn 会产生两个新的版本号.
警告 | |
---|---|
svnmucc 和 svn 的另一个区
别是如果用户没有在命令行提供日志消息 (通过选项
|
svnmucc 的作用不仅仅是混合 svn 的操作, 它还增加了一些其他命令行工具不支持的功能. 例如用户可以使用操作 put 添加或修改仓库里的文件, 把来自本地文件或标准输入 的内容复制到仓库的文件中. svnmucc 还提供了 propset, propsetf 和 propdel 这三种操作, 用于设置或删除文件和目录的属性 (属性值既可以显式地在命令行指定, 也可以从本地文件中读取), 而其他客户端 命令行工具还不支持这些操作 (其他命令行工具只能直接操作工作副本里的文件 的属性).
svnmucc 可以做哪些事, 以及什么事应该由它来做 —这两者的区别非常重要, 先来两句名言:
“给予越多, 期望越多.” |
||
--耶稣 |
“能力越大, 责任越大.” |
||
--Ben, 蜘蛛侠 Peter Parker 的叔叔 |
不使用工作副本的坏处是丧失了冲突检测的能力. 当按照典型的方式使用 svn 时, 被提交的修改是相对于仓库中文件的特定基础 版本, 这样用户就不会无意中覆盖其他用户对相同文件提交的修改. 服务器知道 被用户修改的文件的版本, 也知道在该版本之后是否有其他用户修改了文件, 有了这些, 当用户的提交会破坏其他用户的修改时, Subversion 就会拒绝提交, 强迫用户合并其他用户已提交的修改, 并重新考虑自己的修改. 因为 svnmucc 没有用到工作副本, 也就绕过了冲突检测, 使得 svnmucc 提交的每个修改都是相对于仓库中的最新 版本, 希望这种情况不是用户正想看到的样子.
幸运的是, svnmucc 也希望用户在使用它时能更加地
谨慎, 方法是使用选项 --revision
(-r
).
通过这个选项, 用户可以明确地指定相对于提交的基础版本号, 该版本号应该是
用户在提交前看到的最新的版本号.
警告 | |
---|---|
强烈建议用户总是为 svnmucc 加上选项
|
说明选项 --revision
(-r
) 重要性
的最好方式是展示如何正确地使用命令 svnmucc put.
假设 Harry 想在没有工作副本的情况下修改仓库里的文件
README
(仅针对修改 README
这个操作而言, 我们假设使用工作副本不会带来其他额外的好处, 例如在提交
前执行工作副本里的一个脚本, 以保证 Harry 的修改是符合要求的),
他要确定的第一件事是针对文件的哪个版本进行
修改. 在典型的情况下, 用户总是想修改文件的最新版, 于是 Harry 查询文件
最后一次被提交的版本号, 并把该版本号对应的文件内容抓取到本地的一个临时
文件中.
$ svn info http://svn.example.com/projects/sandbox/README Path: README URL: http://svn.example.com/projects/sandbox/README Relative URL: ^/sandbox/README Repository Root: http://svn.example.com/projects Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68 Revision: 22 Node Kind: file Last Changed Author: sally Last Changed Rev: 14 Last Changed Date: 2012-09-02 10:34:09 -0400 (Sun, 02 Sep 2012) $ svn cat -r 14 http://svn.example.com/projects/sandbox/README \ > README.tmpfile $
现在 Harry 有了 README
的一个副本, 副本的内容
和它最后一次被提交时的内容相同. 他按照自己的想法对副本进行了修改, 修改
完成后, 他打算提交到仓库中.
如果 Harry 单纯地使用 svnmucc put…
,
将仓库中 README
的内容替换成本地修改后的版本,
那他就是在滥用 svnmucc 的能力. 要是在他提交前一
毫秒, Sally 也提交了 README
的修改, 那会怎样?
和 svn 相比, svnmucc 不会为了同时
保留两个用户的修改而尝试在服务器端做一些内容合并操作,
svnmucc 会直接使用指定的内容替换掉文件的最新版本.
Harry 不会察觉到这些, 但 Sally 可能会大发雷霆.
$ svnmucc put README.tmpfile \ http://svn.example.com/projects/sandbox/README \ -m "Tweak the README file." r24 committed by harry at 2013-01-21T16:21:23.100133Z $ Message from sally@shell.example.com on pts/2 at 16:26 ... We need to talk. Now. EOF
Harry 应该回想被自己修改的本地文件来自哪个版本号, 然后把该版本号
通过选项 --revision
(-r
) 传递给命令
svnmucc, 这样服务器就有机会检查被用户修改的文件是否
过旧, 如果是的话就拒绝提交.
$ svnmucc -r 14 put README.tmpfile \ http://svn.example.com/projects/sandbox/README \ -m "Tweak the README file." svnmucc: E170004: Item '/sandbox/README' is out of date $
和 svnmucc 的其他其他选项一样, 选项
--revision
(-r
) 的作用域是整个命令
—命令中指定的每一个操作. 这就使得用户在使用
svnmucc 时, 具有了和下面这种情形同样的保护措施
—检出整个仓库的工作副本 (并且工作副本具有一致的版本号), 修改工作
副本里的文件, 最后提交工作副本的所有修改.
svnmucc 是 Subversion 客户端工具集的有益补充, 它的完整手册见 svnmucc 参考手册—Subversion 多 URL 命令行客户端.