1. 客户端

1.1 相关功能菜单

使用的SVN客户端为TortoiseSVN。

在working copy的根目录或单个文件上,右键菜单中,有create patchapply patch两个功能,分别用于创建patch和使用patch。

截图

1.1.1 create patch

在提交修改前,使用create patch可以生成一个或者多个修改过的文件和当前版本差异的patch(支持目录树)。通常情况下,create patch能把修改保存为.patch.diff文件,.patch.diff文件中记录了发生这个patch的版本号以及具体修改的内容。针对某个文件或某几个文件的若干种修改,可以生成多个.patch.diff文件。

1.1.2 apply patch

生成patch后,可以通过apply patch.patch.diff文件应用到对应版本的working copy中以复刻修改操作。同一个项目/文件夹下,可以选择应用需要的patch,通常来说,应用一个patch时文件版本和生成这个patch时文件的版本是一致的;如果不一致,也可以强制应用,svn会自动进行diff(这时候需要手动合并)。linux下,可以使用系统的patch命令来应用patch,eg: patch -p0 <xxx.patch

1.2 本次commit生成patch

在working copy的修改提交前,可以利用前面介绍的客户端自带的create patch功能,将本次代码修改生成一个patch。然后在任何与修改前同版本的working copy上,通过apply patch将生成的patch进行应用以实现复刻。这么做的优点是只需要维护一个基线版版本,任何基于此的修改和定制不需要维护单独一个全量副本,而是基线+patch的形式,便于维护。

但是目前我所使用的SVN客户端上发现,这么好用的create/apply功能,居然在commit之后就无法生成patch了,你必须在commit之前先create patch,也不能将提交历史中的某个commit生成patch,而后者是开发过程中更常见的场景,于是搜了一些资料,试图寻找将历史提交记录中的某个或连续多个commit生成一个patch的方法。

1.3 多条commit合成patch

SVN客户端中没有找到对应的功能菜单,在网上找到一个解决方案create-svn-patch-after-commit,说是在历史记录中,选中若干个commit,然后右键菜单中找到show differences as unified diff,但是照此方案只能看到diff内容,也不知道怎么根据这个去生成patch文件。

截图
简单地将diff内容复制粘贴到后缀为.patch的文件中,然后尝试在working copy上apply patch,结果是失败的。这个方案还没在我用的SVN客户端上实践成功过,理论上是可行的,不知道是否是字符编码的问题。这个链接中有人提到svn diff可以在任意两个版本之间生成patch,值得一试。

2. 命令行

2.1 相关命令

前面介绍了使用GUI进行patch的种种限制,要发挥svn完整的能力,还得靠命令行。下面介绍的方法是本人实践后确认可行的,通过强大的diff命令几乎可以实现任何patch。完整的生成和使用patch的流程,需要涉及到以下几个命令,分别做简单介绍:

2.1.1 svn log

方法介绍:

log: Show the log messages for a set of revision(s) and/or path(s).
usage: 1. log [PATH][@REV]
       2. log URL[@REV] [PATH...]

这个命令可以用GUI界面替代,不过还是可以了解下。

参数介绍:

  • [PATH]:指定查看对象,默认是.即当前路径;
  • [@REV]:指定具体某次commitID;
  • URL:指定查看远程仓库的对象;

使用示例:

svn log
svn log foo.c
svn log bar.c@42
svn log http://www.example.com/repo/project/foo.c
svn log http://www.example.com/repo/project foo.c bar.c
svn log http://www.example.com/repo/project@50 foo.c bar.c

2.1.2 svn diff

方法介绍:

diff (di): Display the differences between two revisions or paths.
usage: 1. diff [-c M | -r N[:M]] [TARGET[@REV]...]
       2. diff [-r N[:M]] --old=OLD-TGT[@OLDREV] [--new=NEW-TGT[@NEWREV]] \
               [PATH...]
       3. diff OLD-URL[@OLDREV] NEW-URL[@NEWREV]

参数说明(所有参数都不是必填项,所以注意默认值的含义):

  • -r N[:M]:指定比较范围,可以是commitID或commitID范围,比如:-r 100-r 100:110,不填则是指working copy与本地stash之间比较;
  • -c M:与 -r N[:M]是二选一关系,-c M等价于-r M-1:M-c -M等价于-r M:M-1
  • TARGET[@REV]...:指定比较对象,可以是目录、文件或者URL,...说明可以指定0个或多个,同时,如果是URL,那么必须指定-r N[:M]中的N,且M默认为HEAD;

常见场景:

#【重要】对比工作文件与缓存在.svn的“原始”拷贝:
svn diff

#【重要】显示工作文件和服务器版本2的不同:
svn diff -r 2

#【重要】显示分支br1的版本2和版本3的不同:
svn diff file:///home/wwl/svn_repos/branches/br1 -r 2:3
 
#【重要】显示test.java文件在2版本和6版本的区别
svn diff -r 2:6 test.java

#【重要】对比分支br1和trunk区别(2个url)
svn diff file:///home/wwl/svn_repos/branches/br1 file:///home/wwl/svn_repos/trunk 
 
#【了解】直接使用svn diff 显示的不是很清晰,如果本机装了其他的图形diff工具(例如meld)命令行调用:
svn diff --diff-cmd meld

#【了解】svn在1.7版本支持了git方式显示diff
svn diff --git

#【了解】xml方式显示diff
svn diff --xml

利用diff命令生成.diff.patch文件:

# 任何一条diff命令(--git和--xml除外)把结果重定向到文件保存起来即可
svn diff -r 2 > xxx.diff

2.1.3 svn patch

patch: Apply a patch to a working copy.
usage: patch PATCHFILE [WCPATH]

这个命令也比较简单,也可以用GUI去替代使用。

参数介绍:

  • [PATCHFILE]:指定patch文件,可以是.diff或者.patch,可以用diff命令生成;
  • [WCPATH]:working copy path,指定具体要应用patch的工作目录,默认是当前目录;

2.2 本次commit生成patch

根据上面diff命令的说明和示例,这就很简单了:

# 情况一:未commit。直接获取本次改动并生成diff(patch也行)
svn diff > xxx.diff
# 情况二:已经commit。那么先通过svn log或者GUI界面查看最新一次的commitID,然后执行:
svn diff -r commitID > xxx.diff

2.3 多条commit合成patch

# 情况一:连续的commit合成patch
svn diff -r commit1:commit2 > xxx.diff
# 情况二:间断的commit,在每个连续的commit片段上都生成一个个patch,然后按顺序依次应用

3. 参考资料

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐