跳转到主要内容

从文件夹更新svn工作副本。

项目描述

Scunch从外部文件夹更新源代码管理(SCM)系统的工作副本,并根据需要复制、添加和删除文件和文件夹。

使用场景包括

  • 第三方提供的外部源代码的自动版本管理。

  • 通常未进行版本管理的集中资源的自动版本管理,如服务器配置文件。

  • 将基于文件夹的版本管理的项目迁移到适当的SCM。

主要功能包括

  • 灵活的命令行界面,易于自动化。

  • 支持使用ant模式指定要处理的文件。

  • 可选地将文本文件转换为文本,以确保一致性并节省存储空间。

  • 可选地将文件名转换为小写,以防止与大小写敏感的存储库和大小写不敏感的文件系统相关的问题。

  • 在更新工作副本前后执行的可选操作,以确保一致性并防止挂起的更改。

当前支持的系统管理工具(SCM)有

  • Subversion (svn)

“scunch”这个名字是“SCM”的首字母缩写和单词“punch”的组合,通过省略字母使其发音容易。 (早期开发时使用的初始名称是“scmpunch”)。

安装

要安装 scunch,您需要

然后您可以简单地运行

$ easy_install scunch

如果您喜欢手动安装,可以从 <http://pypi.python.org/pypi/scunch/> 获取ZIP存档。此外,源代码可在 <https://github.com/roskakori/scunch> 获取。

要实际使用 scunch,您还需要一个SCM工具。特别是,您需要安装SCM的命令行客户端并将其放置在shell的搜索路径中。安装桌面插件,如 TortoiseSVN,是不够的,因为它没有安装命令行客户端。

以下是安装命令行客户端在流行平台上的提示

  • Mac OS X: svn 包含在Leopard中。或者您可以使用 MacPorts

  • Linux: 使用您的包管理器,例如: apt-get install subversion

  • Windows: 使用 SlikSVN

使用方法

本节简要描述了可用的命令行选项,并附带简单示例。

要查看可用选项的摘要,运行

$ scunch --help

有关实际场景中的更详细用法,请参阅场景部分。

基本用法

要将文件夹 /tmp/ohsome “punch” 到工作副本 ~/projects/ohsome,运行

$ scunch /tmp/ohsome ~/projects/ohsome

要执行相同的操作但还要提交更改,运行

$ scunch --after=commit --message "Punched version 1.3.8." /tmp/ohsome ~/projects/ohsome

控制输出

要控制在“punch”过程中可以看到多少细节,请使用 --log. 要只查看警告和错误,请使用

$ scunch --log=warning /tmp/ohsome ~/projects/ohsome

要查看关于内部工作的大量细节,请使用

$ scunch --log=debug /tmp/ohsome ~/projects/ohsome

--log 的可能值: debuginfo(默认值)、warningerror

指定要处理的文件

默认情况下,scunch 考虑外部文件夹中的几乎所有文件和文件夹以进行传输,排除

  • 由流行的SCM系统使用的内部文件和文件夹,例如 .svn.gitignore

  • 内部系统文件,例如 MacOS X 的 .DS_Store

  • 明显是临时文件,例如 #*#

要忽略额外的文件,请使用 --exclude=PATTERNPATTERN 使用流行的 ant pattern syntax。Ant模式与shell模式类似,支持“*”和“?”占位符。此外,“**”表示任何数量的文件夹和子文件夹,匹配任何文件夹嵌套级别。

例如,要排除所有Python字节码文件,请使用

$ scunch --exclude "**/*.pyc, **/*.pyo" ...

如果您只想处理Python和文件,忽略其他所有内容,请使用 --include

$ scunch --include "**/*.py" ...

当然,您可以将这两个选项结合起来,例如排除所有测试用例之外的Python文件。

$ scunch --include "**/*.py" --exclude "**/test_*.py" ...

有时工作副本包含外部文件夹中永远不存在的文件。例如,工作副本可能包含一个运行 scunch 的脚本,并且已经设置好所有选项。因为这个脚本在外部文件夹中不存在,所以一旦运行 scunch,它就会被删除。为了避免这种情况,请使用 --work-only=PATTERN。例如

$ scunch --work-only "run_scunch.sh" ...

注意,这个例子没有使用“**”占位符,因为只有工作副本顶层文件夹中的文件才感兴趣。

准备工作副本

当从外部文件夹中打取任何更改时,工作副本的当前状态会影响实际发生的情况。

Scunch 在没有挂起更改且文件没有损坏的干净工作副本上运行效果最好。如果不是这种情况,scunch 将拒绝继续并宣布

“…” 中的挂起更改必须提交,使用“svn status”获取详细信息。为了解决此问题,使用‘–before=reset’来放弃更改或使用‘–before=none’来忽略它们。

如果您确定更改无关紧要且打算放弃它们,请使用

$ scunch --before reset ...

这将撤销所有更改并删除未受版本控制的文件。

如果您更喜欢干净检出,请使用

$ scunch --before checkout --depot http://example.com/ohsome/trunk ...

其中 http://example.com/ohsome/trunk 代表项目的仓库限定符。请注意,before=checkout 通常比 --before=reset 花费时间更长,因为检出需要再次获取所有文件,而 --before=checkout 需要获取仓库中的每个文件。

如果您对当前的挂起更改感到满意,并希望在打取外部更改后保留它们,请使用

$ scunch --before none ...

结果可能正是您想要的,也可能不是。

提交打取的更改

要自动提交 scunch 刚刚打取到您工作副本中的更改,请使用

$ scunch --after commit ...

要使用有意义的日志消息执行相同操作,请使用

$ scunch --after commit --message "Punched version 1.3.8." ...

如果您使用脚本启动 scunch 并在完成后想删除工作副本,您可以为 --after 指定多个操作,操作之间用逗号分隔

$ scunch --after "commit, purge" ...

操作按给定顺序执行,请确保将 purge 作为最后一个使用。此外,请注意 "commit, purge" 周围的双引号(“”)。这确保了shell不会将 purge 作为其自己的命令行选项处理。

移动或重命名文件

默认情况下,scunch 会检查添加和删除的具有相同名称但位于不同文件夹中的文件。例如

Added  : source/tools/uitools.py
Removed: source/uitools.py

使用Subversion,scunch 将在内部运行

$ svn move ... source/uitools.py source/tools

而不是

$ svn add ... source/tools/uitools.py
$ svn remove ... source/uitools.py

与添加/删除文件相比,移动文件的优势在于版本历史仍然附着在新文件上。

请注意,这仅适用于文件,但不适用于文件夹。此外,文件名必须完全相同,包括大小写和后缀。

如果您更愿意添加/删除文件而不是移动它们,您可以使用 --move=MODE 指定移动模式。

$ scunch --move=none /tmp/ohsome ~/projects/ohsome

可能的移动模式有

  • name(默认):移动具有相同名称的文件。

  • none:如果移动,则使用添加/删除。

变换名称

尤其是在从第三方获取源文件时,文件或文件夹的某些字母有时会从小写变为大写,或者相反。例如,一个名为some.txt的文件可能在下一次被命名为Some.txt

在区分大小写的文件系统中(例如大多数Unix文件系统),这会导致源代码管理器无法将两个文件联系起来,并开始一个新的变更历史。在区分大小写的文件系统中(如Mac OS X和Windows的标准文件系统),源代码管理器会非常困惑,因为它会认为有两个文件,但文件系统一次只能存储其中一个。

为了防止这种情况,您可以在将文件和文件夹名称打上工作时,告诉scunch进行名称变换。例如

$ scunch --names=lower ...

这会将所有名称转换为小写。

--names的可能值包括

  • preserve(默认):保持名称不变

  • lower:将名称转换为小写;例如,Some.txt变为some.txt

  • upper:将名称转换为大写;例如,Some.txt变为SOME.TXT

请注意,您应该在开始使用新的工作副本时第一次指定--names。当工作副本位于不区分大小写的文件系统上时,仅通过更改字母的大小写来重命名现有文件和文件夹会混淆大多数源代码管理器。

处理非ASCII文件名

为了执行源代码管理操作,scunch作为后台的shell进程简单地运行适当的源代码管理命令行客户端。只要要处理的文件名称仅由ASCII字符组成,这通常运行得很好。一旦您有日文或带有重音符号的名称,问题可能会出现。

默认情况下,scunch会试图自行确定此类情况下的适当设置。然而,这可能会失败,通常结果是UnicodeEncodeError

麻烦的第一个迹象是当scunch记录以下警告消息时

LC_CTYPE应设置为例如‘UTF-8’,以允许处理包含非ASCII字符的文件名

这表明控制台编码设置为ASCII,文件名中的任何非ASCII字符都将导致UnicodeEncodeError。为了解决这个问题,您可以设置环境变量LC_CTYPE来告诉控制台文件名的编码。对于Mac OS X和大多数现代Linux系统,以下命令应该可以解决问题

$ export LC_CTYPE=UTF-8

对于Windows 7,您可以使用

> set LC_CTYPE=UTF-8

请注意,这可能会对其他命令行实用程序产生影响,因此将其设置为.profile.bashrc中的永久设置可能不是好主意。或者,您可以在每次运行scunch时指定适当的编码(这里的大小写无关紧要)

$ scunch --encoding=utf-8 /tmp/ohsome ~/projects/ohsome

对于其他平台,您可以尝试上述值。如果它们没有按预期工作,您需要查阅您文件系统的文档,以了解它使用的编码。

即使编码正确,scunch 和文件系统仍可能在如何规范化 Unicode 字符上产生分歧。再次强调,scunch 尝试确定正确的规范化方式,但如果它出错,您可以使用 --normalize 来指定它。可能的值有:auto(默认值),nfcnfkcnfdnfkd。要了解这些值的含义,请查看 Unicode 联合会的规范化常见问题解答

作为一个完整的示例,Mac OS X 使用 HFS 卷的正确选项是

$ scunch --encoding=utf-8 --normalize=nfd /tmp/ohsome ~/projects/ohsome

顺便提一下,这些就是 scunch 已经使用的值,因此在实际操作中,没有必要明确声明它们。

然而,如果文件位于 UDF 卷上,正确的设置将是

$ scunch --normalize=nfc /tmp/ohsome ~/projects/ohsome

如果用于注入工作副本的外部文件位于与工作副本设置不同的卷上,或者您完全无法确定它们的设置,请尝试将文件复制到具有已知设置的卷上,并在该副本上运行 scunch

场景

本节描述了 scunch 可以大显身手的常见场景。

从旧式版本管理升级

Tim 是一位业余开发者,他一直在编写一个名为“nifti”的实用程序,已经有一段时间了。直到最近,他还没有使用任何版本管理。如果他认为保留源代码的某个状态是有用的,他只需将其复制到一个新的文件夹中,并在文件夹名称中添加时间戳。

$ cd ~/projects
$ ls
nifti
nifti_2010-11-27
nifti_2010-09-18
nifti_2010-07-03
nifti_2010-05-23

经过启发后,他决定将项目迁移到 Subversion 仓库。尽管如此,他还是希望保留所有快照。

作为第一步,Tim 创建了一个本地 Subversion 仓库

$ mkdir /Users/tim/repositories
$ svnadmin create /Users/tim/repositories/nifti

然后,他使用 file 协议添加项目文件夹

$ svn mkdir file:///Users/tim/repositories/nifti/trunk  file:///Users/tim/repositories/nifti/tags  file:///Users/tim/repositories/nifti/branches

现在他可以将 trunk 检出到一个临时文件夹中

$ cd /tmp
$ svn checkout --username tim file:///Users/tim/repositories/nifti/trunk nifti

现在,是时候将最老的版本注入到仍然为空的工作副本中了

$ cd /tmp/nifti
$ scunch ~/projects/nifti_2010-05-23

Tim 检查要提交的更改。不出所料,只有“添加”操作

$ svn status
A   setup.py
A   README.txt
A   nifti/
...

要提交这些更改,Tim 运行

$ svn commit --message "Added initial version."

然后,他继续进行其他版本的提交,其中他让 scunch 自己处理所有提交

$ scunch --commit ~/projects/nifti_2010-07-03
$ scunch --commit ~/projects/nifti_2010-08-18
$ scunch --commit ~/projects/nifti_2010-11-27
$ scunch --commit ~/projects/nifti

现在,所有更改都可以在仓库中清晰地追踪。然而,时间戳使用的是提交的时间,而不是源代码当前的时间。为了解决这个问题,Tim 查看历史记录日志,以找出他更改的修订号以及它们应该代表的实际日期

r1 --> before 2010-05-23
r2 --> 2010-05-23
r3 --> 2010-07-03
r4 --> 2010-08-18
r5 --> 2010-11-27
r6 --> today

为了更新仓库中的时间戳,Tim 设置相应的修订属性 date

$ svn propset svn:date --revprop --revision 2 "2010-05-23 12:00:00Z" file:///Users/tim/repositories/nifti/trunk

请注意,这仅适用于 file 协议。如果您想在使用 http 协议的仓库上执行相同的操作,您必须在该仓库中安装一个适当的 post commit 钩子,以便在提交后更改属性。有关如何操作的详细信息,请参阅 Subversion 手册。

同样,Tim 可以使用修订属性 log 将日志注释设置为更有意义的内容。

一旦仓库准备就绪,Tim 就可以删除他的当前源代码,并用工作副本替换它

$ cd ~/projects
$ mv nifti nifti_backup # Do not delete just yet in case something went wrong.
$ svn checkout file:///Users/tim/repositories/nifti/trunk nifti

现在,Tim 拥有一个可以随时提交更改的版本控制项目。

第三方源代码的版本管理

Joe 在一个 IT 部门工作。他的一个职责是安装名为“ohsome”的第三方开发并交付的 Web 应用的更新。这个工作流程已经定义得很好

  1. 供应商将更新后的源代码以ZIP压缩包的形式发送给Joe,压缩包内包含HTML、JavaScript和XML文件,以及一些服务器配置文件。

  2. Joe将ZIP压缩包解压到本地文件夹。

  3. Joe将本地文件夹的内容移动到服务器上的应用程序文件夹中。在这个过程中,他删除了应用程序的所有旧文件。

只要供应商成功地将所有内容打包到ZIP压缩包中,这种方法就可以正常工作。然而,经验表明,供应商有时会忘记将必要的文件包含在ZIP压缩包中,或者错误地将针对不同站点的配置文件包含在内。尽管这些情况总是可以得到解决,但分析出问题所在并找出哪些文件受到影响却需要花费很长时间。这导致了发布延迟,降低了最终用户满意度,并产生了大量的电话和邮件——包括给管理层的总结。

Joe决定在将更改复制到Web服务器之前先查看一下这些更改。即使他在安装更新之前不能发现错误,SCM也应该有助于他在后续的分析。

Joe的公司已经为各种项目拥有一个Subversion仓库,因此他首先在仓库中添加了一个新项目,并在他的电脑上创建了一个新的工作副本。

$ svn add --message "Added project folders for ohsome application by Vendor." http://svn.example.com/ohsome http://svn.example.com/ohsome/trunk http://svn.example.com/ohsome/tags http://svn.example.com/ohsome/branches

这创建了一个项目文件夹以及通常的trunk、tags和branches文件夹。目前,Joe打算只使用trunk来保存“ohsome”应用程序的最新版本。

接下来,Joe在他的电脑上的本地文件夹中创建了一个空白的工作副本。

$ cd ~/projects
$ svn checkout http://svn.example.com/ohsome/trunk ohsome

现在他将所有文件从Web服务器复制到工作副本中。

$ cp -r /web/ohsome/* ~/projects/ohsome

尽管文件现在在工作副本中,但它们还没有处于版本管理之下。因此,Joe添加了几乎所有文件,除了一个名为“temp”的文件夹,据他所知,这个文件夹只包含由Web应用程序生成的临时文件。

$ cd ~/projects/ohsome
$ svn propset svn:ignore temp .
$ svn add ...

之后,他手动提交了Web服务器的当前状态。

$ svn commit --message "Added initial application version 1.3.7."

目前,Joe已经完成了工作。

几周后,供应商发送了一个包含应用程序版本1.3.8的ZIP压缩包。像往常一样,Joe解压了该压缩包。

$ cd /tmp
$ unzip ~/Downloads/ohsome_1.3.8.zip

结果是创建了文件夹/tmp/ohsome,其中包含要复制到/web/ohsome/下的所有文件和文件夹。然而,这次Joe想在将更改“敲入”到他的工作副本之前先查看更改。因此,他使用以下选项运行scunch

$ scunch /tmp/ohsome ~/projects/ohsome

这将从文件夹/tmp/ohsome(ZIP压缩包解压的地方)将所有更改“敲入”到~/projects/ohsome中的工作副本中。

结果,Joe可以审查这些更改。他使用TortoiseSVN来完成这项工作,但svn statussvn diff也可以。

一旦他完成审查而没有发现任何明显的问题,他手动提交了这些更改。

$ cd ~/projects/ohsome
$ svn commit --message "Punched version 1.3.8."

当1.3.9版本发布时,Joe决定在提交后直接在仓库中审查更改。因此,这次他只是简单地使用

$ cd /tmp
$ unzip ~/Downloads/ohsome_1.3.9.zip
$ scunch --commit --message "Punched version 1.3.9."

Joe可以使用svn log来查找特定的兴趣点。例如,要查找修改后的配置文件(匹配*.cfg模式)

$ svn log --verbose --limit 1 http://svn.example.com/ohsome/trunk | grep "\.cfg$"

以获取已删除的文件和文件夹列表

$ svn log --verbose --limit 1 http://svn.example.com/ohsome/trunk | grep "^   D"

(注意:在此处,grep在行首查找三个空格和一个“D”,表示“已删除”)

许可证

版权(C)2011 - 2012托马斯·阿格拉辛格

本程序是免费软件:您可以在自由软件基金会发布的GNU lesser通用公共许可证的条款下重新分发和/或修改它,许可证版本3,或者(根据您的选择)许可证的任何后续版本。

本程序是根据希望它对您有用的目的进行分发的,但不提供任何保证;甚至没有对适销性或针对特定目的适用性的暗示保证。有关详细信息,请参阅GNU lesser通用公共许可证。

您应该已经收到了与该程序一起的GNU通用公共许可证副本。如果没有,请参阅:https://gnu.ac.cn/licenses/

版本历史

版本 0.5.8,2012-05-08

  • 问题 #21:修复了在3层空文件夹中添加文件的问题。

  • 改进了针对ant模式的调试日志:现在可以通过使用logger 'antglob.pattern'单独设置。

版本 0.5.7,2012-04-08

  • 修复了LC_CTYPE未正确设置时的混乱警告信息。

  • 修复了日志信息,现在可以正确处理非ASCII路径。

  • 通过将一些列表更改为集合来提高性能。

版本 0.5.6,2011-03-04

  • 问题 #20:当工作副本中已存在不符合名称转换的条目时,将--names改为失败。

  • 将传输文本文件的文件属性更改为与源文件相同的属性。

版本 0.5.5,2011-02-28

  • 修复了--before=reset,它没有删除未版本化的已添加文件夹。

  • 清理了代码。

版本 0.5.4,2011-02-23

  • 改进了命令行选项的验证。

  • 清理了错误信息、代码和文档。

版本 0.5.3,2011-02-20

  • 问题 #18:添加了对文件和文件夹名称的转换,例如使用--names=lower

  • 问题 #19:修复了根文件夹中重复处理打孔文件夹的问题。

版本 0.5.2,2011-02-17

  • 问题 #16:修复了在移动文件夹中移动文件时的问题,在此期间包含的文件夹在文件移动之前被删除。

  • 问题 #14:修复了记录的文件处理数量。

  • 问题 #15:为类型为选择型的选项添加了--help的可能值列表。

版本 0.5.1,2011-02-14

  • 问题 #10:添加了--before命令行选项来指定在打孔之前要采取的操作。

  • 添加了在从外部文件夹复制文件之前检查没有待处理更改的检查。使用--before=none来跳过此检查。

  • 问题 #11:添加了--after命令行选项来指定在打孔之后要采取的操作。

  • 删除了命令行选项--commit,使用--after=commit代替。

版本 0.5.0,2011-02-12

  • 问题 #12:添加了--include--exclude选项来指定应该打孔的外部文件夹中的哪些文件。这些选项接受由逗号(,)或空格分隔的ant模式列表。

  • 问题 #13:添加了--work-only选项来指定仅存在于工作副本中但不在外部文件夹中但仍然应该保留的文件和文件夹。如果工作副本包含调用scunch或其他工具的辅助脚本、ant的build.xml、Makefiles等,但永远不会是外部文件夹的一部分,则此功能非常有用。

  • --text更改为使用类似ant的模式而不是后缀列表。例如,现在使用--text="**/*.txt"而不是text=txt

版本 0.4.1,2011-01-09

  • 修复了未指定明确的--encoding时的AssertionError

  • 清理了命令行帮助和代码。

版本 0.4,2011-01-08

添加了用于规范化文本文件的选项并修复了一些关键错误。

  • 问题 #4:添加了--text命令行选项来指定哪些文件应被视为文本,并针对换行符进行规范化。

  • 问题 #5:添加了--newline命令行选项来指定用于文本文件的换行符。

  • 问题 #6:添加了--tabsize命令行选项来指定在文本文件中将制表符对齐到一定数量的空格。

  • 增加了命令行选项 --strip-trailing,用于删除文本文件末尾的空格。

  • 修复了文件名排序问题,可能导致工作副本不一致。

  • 修复了处理类型为‘replace’的内部文件名差异序列的问题,可能导致工作副本不一致。

版本 0.3,2011-01-05

  • 修复了处理 Mac OS X 及可能其他平台上的非 ASCII 字符文件名的问题。

  • 增加了命令行选项 --encoding--normalize,用于指定如何处理非 ASCII 字符。

版本 0.2,2011-01-04

  • 修复了 NotImplementedError

  • 增加了对具有相同名称的文件移动的支持,而不是执行简单的添加/删除操作。这保留了新文件上的版本历史。使用 --move=none 以获取旧行为。

  • 清理了日志输出。

版本 0.1,2011-01-03

  • 初始发布。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分发

scunch-0.5.8.zip (72.0 kB 查看哈希值)

上传时间

由以下组织支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面