SVN 最佳实践 From

http://zoomquiet.org/res/scrapbook/ZqFLOSS/data/20101021150935/index.html

SVN最佳实践一:集中式用户管理

SVN最佳实践二:认证安全和单点登录

SVN最佳实践三:泛路径授权

SVN最佳实践四:授权管理的图形界面

SVN最佳实践五:提交事件邮件通知

SVN最佳实践六:SVN与缺陷跟踪整合

SVN最佳实践七:用钩子实现更多扩展

SVN最佳实践八:Subversion统计

------------------------- 另一篇 -------------------------

From : http://jasonwang168.iteye.com/blog/443754

用过 SVN

在实际使用 SVN 时, 我们需要养成一些良好的习惯, 它们有助于 PL/PM 更好地得到项目信息, 减少不必要的询问/确认这样的交流代价; 也让团队成员之间可以明确地知道彼此的开发活动情况.

  • 开工前先 update . 在开始我们一天的开发活动之前请记得 svn up 一下, 以取出代码库中最新的版本. 因为每天你的团队伙伴们和你一样都在代码树的不同指节上做着开发, 一天结束之后, 代码库里都会增加很多修改, 保持自己本地拷贝的新鲜度, 也许昨天存在的某个影响你的 bug, svn up 之后就已经修好了.
  • 提交前先 status/diff . 每次提交前务必确认自己这次修改所涉及的文件/代码行的变动是 *确切的, 必须的, 没有冗余的*. 我们可以用 svn status 列出所有被修改过的文件; 然后以 svn diff 逐个查看修改过的文件中的代码行变动 *是否确实目的明确的, 有意义的*, 我们有时会不小心加多一个空格, 敲多一个换行, 这在 diff 的时候同样会被列出来, 这些 "变动" 属于不必要的 "噪音", 是不应该被记录到源码库里的, 我们在发现这些 "噪音" 之后就应该把它们清除出去 - 手工恢复原状即可.
  • 细粒度, 经常地, 有目的地提交必要的修改 . 我们每次修改通常都是有明确目的的, 为了新增一个功能, 为了修复一个 bug. 在对代码进行变动的时候, 我们应该保持完成一个目的之后就进行相应的提交的习惯, 要避免把好几件事情都做了 2 ~ 3 天之后再一口气提交这样的做法 (我们会遇到大量代码需要变动的情况, 例如重构, 这时要用 svn 的另外一个功能来完成: branch, 后面会对这个进行说明). 这样通常会使得你的提交过程痛苦一些, 因为这些天已经有了不少变动, 和你的代码产生冲突的机会也递增了. 另外, 这样会使得你的 check in message 变得不好写, 我们下面来说这一块.
  • 提交时一定要写 message . 所有源代码管理软件在提交时都提示 (而非强制) 你要写一段文本, 藉以对这些代码变动进行必要的说明. 我希望给大家建立一个强烈的概念 - 这样一段文本对项目管理非常非常重要, 千万不要偷懒不写或随便写 ! 这段文本的主要目的就一个: 说明我为什么做这些代码变动 . 当每个 changeset 都有对应的 message 的时候, 作为项目的 PL/PM 就可以轻松地从 Trac 的 Timeline 上知道每个团队成员都在为什么目的做什么变动 . 我们再用上 [1234] (changeset), #4321 (ticket) 这样的格式来利用 Trac 的交叉引用功能, 就能达到更好的效果 - PL/PM 可以通过 Timeline 知道哪些 Changesets 之间有关联, 哪些 Changeset 完成了哪些 tickets, PL/PM 可以不用来搔扰你, 打断你的思路就可以知道任务进展情况了; 再者, 写了 check in message, 也方便我们开发人员自己在必要的时候可以快速搜索/定位一些 changesets, 我们有时想回溯/确认某次修改时都做过什么事情, 是否因那次修改引入了某些新的 bug... 这时, 我们在每次 ci 时提交的 message 就会帮到我们, Trac 会对它们进行索引, 我们随时可以通过 Trac 的搜索, 快速找到可能的 Changesets; 对 PL/PM 来说, 这些 message 还有另外一个价值, 那就是在为客户写 Release Notes/Change Logs 的时候, 可以很容易地从这些 message 中整理出来, 而不用回头再搔扰开发人员, 多余地确认你做过的事情. (svn check in message 我们还有自己的约定, 这个另外专题讲述)
  • conflicts 出现时, 务必谨慎地, 细致地进行手工 merge/resolve . SVN 的 copy-modify-merge 模式通常不会带来什么 "麻烦", 我们做 svn update 的大部分时候都会看到自己刚改过的, 尚未提交的文件前头有个 G, 这表示 SVN 聪明地给你自动 mer[G]e 了别人的更新和你当前的改动. 当然, 我们始终会有遇到 [C]onflict 的时候. 但只要遵循了之前的细粒度, 少量的, 多次的提交习惯, 这冲突一般都在 < 20 行内, 很容易可以手工合并的. 遇到冲突时, 我们一定要要非常留心: 千万不要把别人辛勤劳动的结果给覆盖了 . Conflict 是正常的, 可轻松解决的, 不存在别人的改动给自己带来任何麻烦的说法. 解决 conflict 很简单, 我们只需打开处于冲突状态中的文件, 找到类似这样的地方:
    Top piece of bread
    Mayonnaise
    Lettuce
    Tomato
    Provolone
    <<<<<<< .mine
    Salami
    Mortadella
    Prosciutto
    =======
    Sauerkraut
    Grilled Chicken
    >>>>>>> .r2
    Creole Mustard
    Bottom piece of bread

    然 后做出你的技术判断, 看看到底是将两个块合并, 还是择其一而用之, 或是两部分都改动, 最终合并出一个结果. 通常这时候我们还需要和做了 revsion 2 变动的团队成员进行沟通, 确认他的改动的意图 (你也可以直接看 changeset 的 check in message ;), 确认这部分的合并方案.

    有人可能会好奇, 为什么 SVN 这时就这么笨了? 它为什么不直接合并两个修改部分? 其实, 能自动合并的部分, SVN 已经尽责了, 面对这种两个变动发生在同一处的情况, SVN 认为, 这时候该交给更聪明的人类来解决了 ;)

    在我们手工细致合并完毕之后, 记得 svn resolve 这个文件, 然后做 svn ci 即可.

  • svn config. 我们看看一些常见的 svn 的相关配置.
    • EOL; (@TODO: Cheng help me with this part?)
    • ignore: 我们在 svn status 的时候, SVN 会把未加入版本控制的文件项列出来, 然后前面加上 ? 号. 当我们有很多像 .pyc, .tmp, .o 文件的时候, 这些会对我们查看全局的变动情况产生 "视觉噪音". 我们可以通过配置让 svn 无视它们的存在 - 编辑 ~/.subversion/config, 找到 global-ignores, 然后把你不想见到的非版本控制中的文件都列上去, 例如: global-ignores = *.swp *.o *.lo *.la *.pyc sess_* default_* (@TODO: how to windows?)
    • proxy server. svn 还可以配置代理服务器, 编辑 ~/.subversion/servers, 在最后的 [global] 部分里加入两行即可:

      http-proxy-host = www.your-proxy.com

      http-proxy-port = 8888

  • branch, merge (rollback)
    • branch: 我们在进行源代码管理的时候通常还会遇到这样的需求 - 一段时间内需要对原有代码进行大量的改动, 而且这些改动可能会让已有功能不能工作 - 例如重构. 当我们遇到这种情况的时候, 就需要利用 svn 的分支功能, 将现有代码拷贝一份出去进行单独的版本管理. 在那个分支上, 我们就可以进行大刀阔斧的改动, 而不必担心影响 trunk 上已有的, 甚至已经给了用户的代码的稳定性.
    • merge: 我们在一个或多个 branch 上的变更, 到了一定时候, 是可以互相合并的, 包括 branch <-> branch, branch <-> trunk. 例如我们现在对 g20 代码的管理方式是, 到了一个版本, 我们就做一个 branch 出去 (例如, alpha-1.0, alpha-2.0 ...), 而 trunk 保持活跃的开发. 这些独立分出去的代码枝就拥有了自己的生命, 必要时, 我们可以在 branch 上为客户做一些小的修改, 满足一些他们的新需求, 然后再 merge 回 trunk 或者不 merge - 取决于具体情况.

      merge 除了做分支间的 "大动作" 的合并之外, 还可以做单个文件的 revision 之间的合并, 我们可以利用这个功能, 在提交了错误的 changeset 之后, 用 merge 来 undo 你的修改, 例如, revision 1234 是我提交错了的, 那么, 我们只需 svn merge -r 1234:1233 就可以 undo 了, 当然, 最后还需要 svn ci 一次 :)

  • use google code to practice: 要练习 svn 的各种命令, 可以用我们专门为学习 svn 创建的一个 Google Code Project 来无风险地练练, 或者自己装上 subversion :)
  • GUI (小乌龟, esvn) (@TODO: Cheng help me with this?)
  • 常用简写/参数:
    • svn ci = svn commit
    • svn st = svn status
    • svn up = svn update
    • --dry-run: 假装跑一次, 让我们知道什么文件会被更新. 我们在 svn ci, svn up 等命令的时候可以加入 --dry-run 这个参数, 以便测试实际哪些文件会被动到.

SVN Check In Message Conventions

Svn check in format:

  <Prefix>: <message goes here>.

The check in message should be short but straight forward for others to understand. We need to ensure that: other developer or the project lead know exactly what you did in this changeset once they read your message, and they don't have to ask you for further explaination. (@TODO: benifits)

Prefixies :

  • ADD (Add): adding directories, files
  • BRN (Branch): branching
  • CMT (Comment): comments added to code
  • DEL (Delete): removing directories, files
  • DOC (Document): document related changes
  • FIX (Fix): bug fixing
  • FMT (Format): Code formating changes
  • INI (Initial): initial import of a project
  • MRG (Merge): merging from revision to revision, branch to trunk, etc.
  • REV (Revision): stable/small changes
  • WIP (Work In Progress): not yet finished changes, for example:
    • you need to check in your code before other developer could work on it for a feature;
    • you need to check in since it's 18:00, you would like to work on it tomorrow, or
    • you need to check in before making to many changes

How to Deal With System-specific Configuration Files

在一个项目的代码中,通常有数个配置文件也被置于版本控制之下。然而,这些文件的特点在于,其中有些配置项在每个系统上是不一样的,例如,每个开发人员 checkout 的目录就不一样,部署到外部服务器上也不一样,如果这个文件仍旧和其他代码文件一样对待,首先在 ci 时就得时时提醒开发人员不要提交你的本地定制,二是每次 svn stat 都会看到配置文件前面有个 M 这样的“噪音”,三则一旦不小心提交了,所有人自己的定制都面临可能有冲突的麻烦,尤其生产服务器上的更是不能随便出问题。

以下解决方案来自我们一位客户的经验,值得我们学习!

We typically identify files that contain system-specific configuration information, and name the versioned copies of the files with a supplemental .default suffix. Such files contain clearly-generic values for system paths, DB connection parameters, and the like.

We then make a copy of this file without the suffix, and set up the `svn:ignore` parameters in the parent directory to ignore that file.

Let's examine this approach using a particular file from the project:

/trunk/src/cake_project/app/config/rebuild_database.sh

Assuming I've checked out the repository to $client's `DEV` server filesystem, and have cd`d into app/config in the project files, here's what the process looks like from a terminal session:

$ svn mv ./rebuild_database.sh ./rebuild_database.sh.default

A rebuild_database.sh.default

D rebuild_database.sh

$ svn propset svn:ignore 'rebuild_database.sh' .

property 'svn:ignore' set on '.'

$ svn commit . -m 'rebuild_database.sh is no longer under danger of wildly reverting commits.'

From here, I could continue to edit rebuild_database.sh as frequently (or not) as necessary, and the file--changed or not--will never show up in svn status queries, nor will changes to its contents ever be committed into the repository.