12 Apr 2012

Why Git is better than SVN

在版本控制系统的选型上,是选择Git还是SVN?

对于开源项目来说这不算问题。使用Git极大地提高了开发效率、扩大了开源项目的参与度、 增强了版本控制系统的安全性,选择Git早已是大势所趋。

但对于企业用户来说这个决心不太好下。部分原因是出于对Git的误解,部分原因是尚不了解 Git到底能给项目管理带来什么好处。希望本文能对您项目的版本控制系统选型提供帮助。

对SVN的迷信和对Git的误解

误解1:SVN只能检出(checkout)一个版本(revision)的代码,而Git却可以脱库!

这个误解是如此普遍,简直成了SVN在企业市场中封杀Git的尚方宝剑。其实稍微思考一下 这个谣言就很难传播。既然SVN能够读取授权访问的文件的每一个版本,那么就能够重组这些版本, 进而实现对版本库的完整复制。即SVN也可以脱库。

SVN脱库的工具SVN本身就提供: svnsync 。这个工具主要用于SVN的版本库镜像。 例如将版本库 http://host.name/svn/repo 脱库到本地的 dump 目录,命令如下:

$ svnadmin create dump
$ printf '#!/bin/sh\nexit 0\n' > dump/hooks/pre-revprop-change
$ chmod a+x dump/hooks/pre-revprop-change
$ svnsync init file://$(pwd)/dump http://host.name/svn/repo
$ svnsync sync file://$(pwd)/dump

如果使用 git-svn 则为SVN“脱库”更简便。

$ git svn clone -s http://host.name/svn/repo dump

有人认为SVN可以对目录授权,从而阻止对整个版本库进行脱库操作。 下面就来看看SVN的授权究竟是否可靠。

误解2:SVN能对目录进行精细授权,而Git太不安全

SVN的目录授权对管理员来说是灾难,管理负担相当重,在分支或里程碑众多的时候很难作对。 这是因为SVN的分支和里程碑(tags)本身就是一个目录(使用目录拷贝实现的)。

例如管理员为名为demo的SVN版本库授权。一个并不太复杂的主线(/trunk)授权如下:

[demo:/trunk]
@demo-admin = rw
@leaders = r

[demo:/trunk/doc]
@demo-dev = rw
@designers = rw

[demo:/trunk/src/apps]
@demo-dev = rw

[demo:/trunk/src/common]
@demo-dev = rw

[demo:/trunk/src/html]
@designers = rw

[demo:/trunk/src/secret]
* =
@demo-admin = rw
jiangxin = rw

如果项目创建了维护分支 /branches/1.x ,若和 /trunk 授权相同,则需要将上述授权在 /branches/1.x 下重建。需要在授权文件中再添加如下授权指令:

[demo:/branches/1.x]
@demo-admin = rw
@leaders = r

[demo:/branches/1.x/doc]
@demo-dev = rw
@designers = rw

[demo:/branches/1.x/src/apps]
@demo-dev = rw

[demo:/branches/1.x/src/common]
@demo-dev = rw

[demo:/branches/1.x/src/html]
@designers = rw

[demo:/branches/1.x/src/secret]
* =
@demo-admin = rw
jiangxin = rw

如果版本库的分支和里程碑越来越多,配置的工作量相当可观,稍有不慎不是授权文件格式破坏导致SVN无法工作, 就是造成开放授权。

我曾经写过SVN路径授权的补丁,并写了一款SVN版本库管理的开源软件 (参见 《pySvnManager手册》 ), 但想完美解决这个问题很难。我的一个设想是在SVN对分支和里程碑授权检查时缺省使用 /trunk 的授权,但这样的实现要求使用SVN严格遵循约定俗成的三个顶级目录的规范。

Git对于写操作可以精细到目录和分支级别(使用Gitolite作为服务器), 但作为分布式版本库控制系统,在设计上只能实现版本库量子化的读授权。 即某用户对整个版本库要么都能读,要么对整个版本库都不能读。

那么如何控制Git版本库的读授权呢?实际上Git可以通过子模组来实现细粒度的读授权。 即在项目需要精细授权的场合,将版本库拆分为多个Git版本库进行单独授权, 再使用子模组将多个版本库整合为一个。这个操作并不复杂,而且有助于实现项目的模块化。

误解3:Git能随意改变历史提交,这对于版本控制来说是不合适的

Git对历史提交的修改只对本地提交有意义。本地提交就像是和共享版本库间的缓冲。 在未将本地提交推送到远程共享版本库之前,开发者可以后悔。可以对不完整的提交说明进行补充, 可以移除错误的提交,可以压缩合并提交等。Git对提交历史灵活的操作是Git独有的功能, 是提交审核的必备工具。

对于已经推送到远程共享服务器的提交,Git就不能再像本地一样随意更改了。 因为推送到共享版本库的提交一旦被其他程序员获取,便扩散出去, 如覆水难收,难掩众人悠悠之口。所以Git更改历史提交只对本地有效,是安全的。

相比之下,SVN本地工作区和集中式版本库之间没有缓冲,一旦发现提交了错误内容, 或写了错误的提交说明,则无法更改,除非SVN管理员介入。 SVN也允许配置为可修改历史提交说明,但是一旦管理员放开此功能, 历史提交的提交说明有可能被批量、恶意更改,并且无法恢复。

误解4:SVN对中文支持更好,Git库中的中文目录和文件名会出现乱码

我也曾经这么认为,并在《Git权威指南》第3章中用了大量篇幅介绍中文支持的注意事项。 并推荐使用Cygwin作为首选客户端,以避免GBK字符集为跨平台开发的版本库引入乱码。

一个好消息是Windows下最常用的Git客户端 msysGit 也支持Unicode了。 使用最新版本(1.7.10)的 msysGit 无需设置任何Git配置变量, 版本库中的中文文件名、目录名、提交说明都使用Unicode编码。 配合使用Unicode版的TortoiseGit,Windows用户就不再为跨平台开发的字符集问题而伤脑筋了。

误解5:SVN的认证方式比Git丰富,比如可以实现LDAP认证

我为客户配置的Git支持HTTP、SSH协议,和Gitweb。其中HTTP协议、Gitweb都使用LDAP认证, 实现统一的口令管理。并且无论是HTTP协议、SSH协议,还是Gitweb都使用同一套Gitolite授权。

误解6:SVN更易上手,Git太难了

如果想把配置管理做好,SVN并不容易,否则 《SVN Book》 也不会有那么厚了。

  • 很多公司的SVN版本库没有遵照约定俗成的三个顶级目录。
  • 如何配置SVN悲观锁,以便更好地对二进制文件编辑进行协同。
  • 维护合并追踪的 svn:mergeinfo 属性,以便能够正确的分支合并。还要防止无此功能的客户端对其的破坏。
  • SVN如何正确的反删除,直接添加删除的文件是不对的。
  • 如何使用 svn:eol-style 属性,以便正确处理跨平台开发时的文件换行符问题。
  • SVN管理员如何对版本库进行整理,如撤出不当提交、修改错误的提交说明。
  • 版本库的安全性问题,如何做好版本库的备份。

Git的设计模型非常简单,理解了其设计思想,就可以很容易地掌握 git reset, git checkout, git rebase, git push, git pull 等命令。

误解7:程序员不喜欢命令行

谁说Git没有好的图形工具?SVN 有 TortoriseSVN,Git 同样有 TortoiseGit。 只不过Git的命令行太好用,使得图形操作显得笨拙。

至于Windows用做开发环境是否还有前途,看看火热的iOS、Android开发、和优雅的 MacBook 就知道了。

Git能做到,而SVN难以做到的事情

Git分支功能最为强大,分支管理能力让SVN望尘莫及

Git可以很容易地对比两个分支,知道一个分支中哪些提交尚未合并到另一分支,反之亦然。

  • 查看当前分支比other分支多了哪些提交:

      $ git log other..
    
  • 查看other分支比当前分支多了哪些提交:

      $ git log ..other
    

我不认为SVN的分支是真正的分支,因为分支最基本的提交隔离SVN就没能实现。 在SVN中一次提交可以同时更改主线(/trunk)和分支中的内容, 所以判断一个分支中哪些提交未合并到另外的分支,完全不能对SVN抱有希望。

Git可以实现更好的发布控制

针对同一个项目,Git可以设置不同层级的版本库(多版本库), 或者通过不同的分支(多分支)实现对发布的控制。

  • 设置只有发布管理员才有权限推送的版本库或者分支,用于稳定发布版本的维护。
  • 设置只有项目经理、模块管理员才有权推送的版本库或者分支,用用于整合测试。

隔离开发,提交审核

如何对团队中的新成员的开发进行审核呢?在Git服务器上可以实现用户自建分支和自建版本库的功能, 这样团队中的新成员既能将本地提交推送到服务器以对工作进行备份, 又能够方便团队中的其他成员对自己的提交进行审核。

审核新成员提交时,从其个人版本库或个人分支获取(fetch)提交,从提交说明、代码规范、编译测试 等多方面对提交逐一审核。审核通过执行 git merge 命令合并到开发主线中。

对合并更好的支持,更少的冲突,更好的冲突解决

因为Git基于对内容的追踪而非对文件名追踪,所以遇到一方或双方对文件名更改时, Git能够很好进行自动合并或提供工具辅助合并。而SVN遇到同样问题时会产生树冲突, 解决起来很麻烦。

Git的基于DAG(有向非环图)的设计比SVN的线性提交提供更好的合并追踪, 避免不必要的冲突,提高工作效率。这是开发者选择Git、抛弃SVN的重要理由。

保证已修复Bug不再重现

以为创建完毕里程碑标签(tag)便完成软件版本的发布是有风险的, 往往会由于之前的版本(维护版本)中的一些 Hotfix 提交没有合并到最新版本而造成已修复问题在新版本中重现。

Git分支和合并追踪可以解决这个问题。例如用 maint 分支跟踪最新的发行版, 当确定里程碑tag v1.6.4 为最新发行版时,在 maint 分支执行如下命令以切换到最新发行版:

$ git checkout maint
$ git merge --ff-only v1.6.4

如果合并成功,代表发行版 v1.6.4 包含了所有前一个发行版的提交。 反之说明前一个发行版某个或某些Hotfix提交尚未合并到最新发行版中。

版本库的安全性

SVN版本库安全性很差,是管理员头痛的问题。

  • SVN版本库服务器端历史数据被篡改,或者硬盘故障导致历史数据被篡改时, 客户端很难发现。管理员的备份也会被污染。
  • SVN作为集中式版本控制系统,存在单点故障的风险。备份版本库的任务非常繁重。

Git在这方面完胜SVN。首先Git是分布式版本控制系统,每个用户都相当于一份备份, 管理员无需为数据备份而担心。再有Git中包括提交、文件内容等都通过SHA1哈希保证数据的完整性, 任何恶意篡改历史数据都会被及时发现从而被挫败。

更多的十条喜欢Git的理由

更多的十条喜欢Git的理由,参见 《Git权威指南》 第11-21页。

  • 异地协同工作。
  • 现场版本控制。
  • 重写提交说明。
  • 无尽的后悔药。
  • 更好用的提交列表。
  • 更好的差异比较。
  • 工作进度保存。
  • 作为SVN前端实现移动办公。
  • 无处不在的分页器。
  • 快。

什么情况推荐使用SVN

SVN具有的悲观锁的功能,能够实现一个用户在编辑时对文件进行锁定,阻止多人同时编辑 一个文件。这一悲观锁的功能是 Git 所不具备的。对于以二进制文件 (Word文档、PPT演示稿) 为主的版本库,为避免多人同时编辑造成合并上的困难, 建议使用SVN做版本控制。

View Comments
29 Mar 2012

New hack for topgit: git-merge--no-edit

Git 1.7.10 对 git merge 提供了一个改进,而这个改进可能会带来兼容性问题, 会导致某些依赖 git merge 自动完成的工具受到影响。而 Topgit 就中招了。

Junio 的 一篇博客 专门对这个问题做了描述。简单的说就是Git 1.7.10 起, 执行 git merge 时,成功的合并不会使用默认的(没有意义的)提交说明自动提交, 而是会打开一个编辑器等待用户输入提交说明。

这个改动会使得调用 git merge 的工具在执行时被打断。Topgit 的 tg update 等命令即受此影响。

这个 GitHub上的提交 即用于解决此问题:

From: Jiang Xin <worldhello.net@gmail.com>
Subject: [PATCH] No stop to edit for the new merge behavior of git

In Git 1.7.10, Merge will stop and wait for a merge commit log. This
backward-incompatible improvement that will break topgit. To fix it,
just export GIT_MERGE_AUTOEDIT=no.

See: http://git-blame.blogspot.jp/2012/02/anticipating-git-1710.html

Signed-off-by: Jiang Xin <worldhello.net@gmail.com>

---
 tg.sh |    2 ++
 1 个文件被修改,插入 2 行(+)

diff --git a/tg.sh b/tg.sh
index 9082d88..b7661c2 100644
--- a/tg.sh
+++ b/tg.sh
@@ -430,6 +430,8 @@ get_temp()
 ## Initial setup

 set -e
+# suppress the merge log editor feature since git 1.7.10
+export GIT_MERGE_AUTOEDIT=no
 git_dir="$(git rev-parse --git-dir)"
 root_dir="$(git rev-parse --show-cdup)"; root_dir="${root_dir:-.}"
 # Make sure root_dir doesn't end with a trailing slash.
-- 
tg: (d279e29..) t/git-merge-no-edit (depends on: tgmaster)
View Comments
19 Mar 2012

Git测验(A卷)

在深圳做Git培训和项目管理软件实施,这是一份Git培训的测验题,通过率不高。因为不合格的还要重考,答案就不公布了。

单项选择题

  1. 如果提示提交内容为空、不能提交,则最为合适的处理方式是:_____

    a) 执行 git status 查看状态,再执行 git add 命令选择要提交的文件,然后提交。

    b) 执行 git commit --allow-empty ,允许空提交。

    c) 执行 git commit -a ,提交所有改动。

    d) 执行 git commit --amend 进行修补提交。

  2. 如果把项目中文件 hello.c 的内容破坏了,如何使其还原至原始版本? _____

    a) git reset -- hello.c

    b) git checkout HEAD -- hello.c

    c) git revert hello.c

    d) git update hello.c

  3. 修改的文档 meeting.doc 尚未提交,因为错误地执行了 git reset --hard 导致数据丢失。丢失的数据能找回么? _____

    a) 不能。执行硬重置使工作区文件被覆盖,导致数据丢失无法找回。

    b) 能。可以通过 git checkout HEAD@{1} -- meeting.doc 找回。

    c) 不确定。如果在重置前执行了 git add 命令将 meeting.doc 加入了暂存区,则可以在对象库中处于悬空状态的文件中找到。

    d) 不能。因为未提交所以无法找回。

  4. 仅将工作区中修改的文件添加到暂存区(新增文件不添加),以备提交,用什么命令标记最快? _____

    a) git add -A

    b) git add -p

    c) git add -i

    d) git add -u

  5. 下面哪一个命令 会改变提交历史? _____

    a) git reset --hard HEAD~1

    b) git checkout HEAD^^ .

    c) git rebase -i HEAD^^

    d) git commit --amend

  6. 我使用和其他人不一样的IDE软件,总是在目录下生成以 .xx 为后缀的临时文件。如何避免由于自己的误操作导致此类文件被添加到版本库中呢? _____

    a) 执行 git clean -f 删除临时性文件。

    b) 向版本库中添加一个 .gitignore 文件,其中包含一条内容为 *.xx 的记录。

    c) 在文件 .git/info/exclude 中添加一条内容为 *.xx 的记录。

    d) 更换另外一款IDE软件。

  7. 项目跨平台导致文件中的换行符不一致。其中有 Linux 格式换行符(0A),也有DOS格式换行符(0D 0A)。要如何避免此类情况呢? _____

    a) 修改 /etc/gitattributes 文件,在其中包含一条内容为 * text=auto 的设置。

    b) 执行命令 git config --global core.autocrlf true

    c) 执行命令 git config --global core.autocrlf input

    d) 向版本库中添加一个 .gitattributes 文件,在其中包含一条内容为 * text=auto 的设置。

  8. 下列对于版本库授权说法正确的是:_____

    a) 可以为分支或路径设置不同的写入权限,但不能设置不同的读取权限。

    b) 除管理员外,版本库的创建者都可以为自己创建的版本库授权。

    c) 只要通过授权后,便不能限制所推送的提交的署名作者,可以是任何人。

    d) 如果没有向版本库的写入权限,就一定没有读取权限。

  9. 取消服务器版本库中ID为 a2387 的提交,而且不能引起历史提交的变更,用什么操作? _____

    a) git rebase -i a2387^

    b) git checkout a2387^ -- .

    c) git revert a2387

    d) git reset --hard a2387^

  10. 从版本库中的历史提交中彻底移除ID为 a2387 的提交,用什么操作? _____

    a) git reset --hard a2387^

    b) git checkout a2387^ -- .

    c) git revert a2387

    d) git rebase --onto a2387^ a2387 HEAD

  11. 所有改动的文件都已加入暂存区,若希望将其中的 other.py 文件下次再提交,如何操作? _____

    a) git reset -- other.py

    b) git checkout -- other.py

    c) git checkout HEAD other.py

    d) git reset --hard -- other.py

  12. 若产品的版本号显示为 1.7.10.rc0-33-g9678d-dirty ,可以判断出此版本号是如何生成的么? _____

    a) git tag

    b) git describe --tags --always --dirty

    c) git name-rev HEAD

    d) git --version

  13. 关于 git clone 下面说法 错误 的是:_____

    a) 克隆时所有分支均被克隆,但只有HEAD指向的分支被检出。

    b) 可以通过 git clone --single-branch 命令实现只克隆一个分支。

    c) 克隆出的工作区中执行 git loggit statusgit checkoutgit commit 等操作不会去访问远程版本库。

    d) 克隆时只有远程版本库HEAD指向的分支被克隆。

  14. 关于删除分支 XX ,下列说法正确的是: _____

    a) 执行 git push origin :XX 来删除远程版本库的 XX 分支。

    b) 执行 git branch -D XX 删除分支,总是能成功。

    c) 远程版本库删除的分支,在执行 git fetch 时本地分支自动删除。

    d) 本地删除的分支,执行 git push 时,远程分支亦自动删除。

  15. 下面的操作中哪一个不能确认维护分支 maint 上所有的 bugfix 提交均已合并至当前分支 master 中。 _____

    a) git rev-list ..maint 的输出为空。

    b) 在 maint 分支成功地执行 git merge master

    c) git log ..maint 的输出为空。

    d) 新版本发布,在 maint 分支执行 git merge --ff-only master 成功。

  16. 一个图片文件 logo.png 冲突了,如何取出他人的版本。 _____

    a) git show :1:./logo.png > logo.png-theirs

    b) git show :2:./logo.png > logo.png-theirs

    c) git show :3:./logo.png > logo.png-theirs

    d) git show :0:./logo.png > logo.png-theirs

  17. 工作在特性分支,常常因为执行 git push 默认推送所有本地和远程共有分支,导致非当前分支报告 non-fast-forward 错误。如果设置只推送当前分支可避免此类问题。下面操作正确的是:_____

    a) git config --global push.default upstream

    b) git config --global pull.rebase true

    c) git config --global receive.denyDeletes true

    d) git config --global pager.status true

  18. 关于对象库(.git/objects)说法 错误 的是:_____

    a) 两个内容相同文件名不同的文件,在对象库中仅有一个拷贝。

    b) 对象库执行 git gc 操作后,reflog 会被清空导致其中记录的未跟踪提交及指向的文件被丢弃。

    c) 删除文件后,再通过添加相同文件找回,不会造成版本库的冗余。

    d) 对象库并非一直保持最优存储,而是通过周期性地执行 git gc 优化版本库。

  19. 关于子模组 错误 的说法是:_____

    a) 克隆父版本库,默认不会克隆子模组版本库。

    b) 子模组可以嵌套。执行 git submodule update --recursive 可对嵌套子模组进行更新。

    c) 子模组和父版本库的新提交,要先推送父版本库,后推送子模组。

    d) 子模组检出处于分离头指针状态(gitlink的指向),在子模组中工作需要手动切换分支。

  20. 当一个提交说明显示为 souce code refactor (fix #529) ,下面哪个说法是正确的? _____

    a) 这个提交只是代码重构,并未修复任何东西,因此没有改变版本库的提交历史。

    b) 这个提交修正了第529号提交,没有改变版本库的提交历史。

    c) 这个提交撤销了第529号提交,改变了版本库的提交历史。

    d) 这个提交和项目的缺陷跟踪平台(如Redmine)关联,并会更新相关问题的状态。


View Comments
28 Feb 2012

Git中文本地化

Git从版本1.7.5(2011年4月)即开始国际化/本地化(i18n/l10n)的准备工作,到版本1.7.8正式完成了Git国际化/本地化框架和基础设施的工作。 参见 提交 5e9637c

针对部分语种的本地化工作已经开始,最早会在 Git 1.7.10 版本开始提供中文的本地化支持。

Git中文本地化注意事项

Git本地化(包括中文本地化)的协同方式不同于Git项目本身,不使用邮件列表进行提交评审,而是通过GitHub的协同工具来完成,这在Git邮件列表中有专门的讨论: Git官方邮件列表 。最终确定的工作流程参见Git源码中的文件 po/README

工作流程

中文本地化的协同版本库为: https://github.com/gotgit/git-po-zh_CN/ ,以下简称 git-po-zh_CN 。 如果您对翻译有修改或补充,请参照如下工作流程。

  1. 使用 GitHub 的 Fork 功能从 git-po-zh_CN 派生项目。

  2. 从您个人派生的版本库克隆。如:

     git clone git@github.com:<your-account>/git-po-zh_CN.git
     cd git-po-zh_CN
    
  3. 修改 po/zh_CN.po 文件,补充和完善翻译内容,并提交。

     edit po/zh_CN.po
     ...
     git add -u
     git commit
    
  4. 将您的提交推送至GitHub(您的派生版本库)。

     git push
    
  5. 使用GitHub提供的 pull request 功能,创建到 git-po-zh_CN 版本库的 Pull Request。

  6. 版本库 git-po-zh_CN 的管理者审核您的 Pull Request,若审核通过则将提交合并到代码树中,若不通过则会给出原因。

提交规范

本地化参与者在创建 Pull Request 前,请先确认提交是否遵守下列规范:

  • 只能修改 po/zh_CN.po 文件,或者在 po/TEAMS 文件中补充中文翻译团队成员列表。若对其他文件进行改动,会导致 pull request 被拒绝。

  • 提交说明符合 “50/72 原则”。

    • 提交说明第一行会作为补丁邮件的标题或者作为简要日志输出,长度以50个字符为限。

      建议这部分提交说明使用 l10n: 作为前缀,以便和Git其他代码的提交相区分。

      还有注意这部分内容不能包含中文。

    • 一个空行

    • 关于提交的大段说明,例如原翻译存在的问题,为什么要进行修改等。

      提交说明可以折行,每行以72个字符为限。

      这部分提交说明可以包含少量中文。

  • 提交说明中包含签名。可以通过命令 git commit -s 会自动在提交说明中添加签名。

翻译示例

  • C语言字符串中的参数(占位符)很重要,需要维持其顺序。

    注意字符串中出现的两个占位符 %s%s 的先后顺序要严格保持。

      #: builtin/pack-objects.c:2326
      #, c-format
      msgid "unable to parse value '%s' for option %s"
      msgstr "不能解析值 '%s' 针对于选项 %s"
    
  • 然而,Shell语言字符串中的变量(占位符)的顺序则不重要,可以根据翻译需要调整先后次序。

      msgid "Clone of '$url' into submodule path '$path' failed"
      msgstr "无法克隆 '$url' 到子模组路径 '$path'"
    
  • 大多数情况下,英文标点翻译为中文标点。如:

      msgid ""
      "'%s' appears to be a git command, but we were not\n"
      "able to execute it. Maybe git-%s is broken?"
      msgstr ""
      "'%s' 像是一个 git 命令,但却无法运行。\n"
      "可能是 git-%s 受损?"
    
  • 但是有的字符串要由程序进行后处理,并且处理过程中需要依赖其中的标点符号,这种情况下标点符号不能翻译,要严格保持。如:

      #  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
      #: wt-status.c:250
      msgid "modified content, "
      msgstr "修改的内容, "
    
  • 大多数情况下,要注意保持原文中句子前、后,以及句子中间的空白。大多是排版的需要。

      #  译者:注意保持前导空格
      #: diff.c:105
      #, c-format
      msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
      msgstr "  无法解析 dirstat 阈值 '%.*s'\n"
    
      #  译者:为保证在输出中对齐,注意调整句中空格!
      #: wt-status.c:266
      #, c-format
      msgid "new file:   %s"
      msgstr "新文件:    %s"
    
      #  译者:为保证在输出中对齐,注意调整句中空格!
      #: wt-status.c:269
      #, c-format
      msgid "copied:     %s -> %s"
      msgstr "拷贝:      %s -> %s"
    
      #  译者:注意保持句尾空格
      #: wt-status.c:890
      msgid "Initial commit on "
      msgstr "初始提交于 "
    
  • 但有的情况下涉及到两个翻译为中文的字符串拼接,原文中句子前、后包括句中的空白需要在译文中删除。

      #  译者:汉字之间无空格,故删除尾部空格
      #. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
      #: builtin/branch.c:163
      msgid "remote "
      msgstr "远程"
    
      #  译者:%s若翻为中文,前后不需要空格
      #: builtin/commit.c:783
      #, c-format
      msgid ""
      "\n"
      "It looks like you may be committing a %s.\n"
      "If this is not correct, please remove the file\n"
      "\t%s\n"
      "and try again.\n"
      msgstr ""
      "\n"
      "看起来您正在提交一个%s。\n"
      "如果不是这样,请删除文件\n"
      "\t%s\n"
      "然后重试。\n"
    
  • 大多数情况下,考虑到输出的美观,需要重新对译文字符串进行排版(重新设置换行符)。

      #: builtin/commit.c:42
      msgid ""
      "Your name and email address were configured automatically based\n"
      "on your username and hostname. Please check that they are accurate.\n"
      "You can suppress this message by setting them explicitly:\n"
      "\n"
      "    git config --global user.name \"Your Name\"\n"
      "    git config --global user.email you@example.com\n"
      "\n"
      "After doing this, you may fix the identity used for this commit with:\n"
      "\n"
      "    git commit --amend --reset-author\n"
      msgstr ""
      "您的姓名和邮件地址基于登录名和主机名进行了自动设置。请检查它们正确\n"
      "与否。您可以通过下面的命令对其进行明确地设置以免再出现本提示信息:\n"
      "\n"
      "    git config --global user.name \"Your Name\"\n"
      "    git config --global user.email you@example.com\n"
      "\n"
      "设置完毕后,您可以用下面的命令来修正本次提交所使用的用户身份:\n"
      "\n"
      "    git commit --amend --reset-author\n"
    
  • 但有时保持原有换行方式更好。

    下面的示例中如果译文将两行合并为同一行,会因为两处的 %s 被扩展造成输出太长。

      #  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
      #: builtin/branch.c:137
      #, c-format
      msgid ""
      "deleting branch '%s' that has been merged to\n"
      "         '%s', but not yet merged to HEAD."
      msgstr ""
      "将要删除的分支 '%s' 已经被合并到\n"
      "         '%s',但未合并到 HEAD。"
    

术语表

已翻译内容中涉及到的术语的翻译做到了基本一致,若遇到不确认的术语请先在已翻译内容中查找,恕不一一列举。

为什么有的Git命令的输出还没有翻译?

如果遇到您需要的Git命令尚未翻译,往往不是因为中文本地化翻译未完成,而是因为相应命令的输出尚未进行国际化标识。 Git 的国际化(i18n)和本地化(l10n)刚刚开始,肯定存在很多命令输出被遗漏,没有使用相关语法进行标识,导致未能在本地化文件中出现。

例如我发现和解决的几处Git国际化遗漏的问题:

Git国际化不完整的问题不能采用本文介绍的中文本地化工作流程进行操作,而是按照修改Git源代码的工作流程进行工作。 具体参见 Documentation/SubmittingPatchest

View Comments

更多博客

Gitolite 管理员自定义命令 30 Nov 2011 Comments
Gitolite 通配符版本库自定义授权 30 Nov 2011 Comments
Gitolite 版本库镜像 30 Nov 2011 Comments
Gitolite 客户端发起安装模式被取消 30 Nov 2011 Comments
Git权威指南 Gitolite 章节更新 30 Nov 2011 Comments
用 Git 维护博客?酷! 29 Nov 2011 Comments
Gist数据嵌入博客 14 Sep 2011 Comments
GitHub新书通告及邀请您关注微博账号 29 Aug 2011 Comments
版本库中一个大家都要改的文件,又不想每次提交而覆盖,怎么办? 10 Jun 2011 Comments
Git版本库同步对部分版本库禁用 02 Jun 2011 Comments
《Git权威指南》官方网站上线 20 May 2011 Comments
apt-cacher-ng: 万能软件包代理 20 Apr 2011 Comments
Repo 新增 hack:URL 自动 DotGit 后缀控制等 19 Apr 2011 Comments
给RPM软件包签名 12 Apr 2011 Comments
Linux下的通用打开命令 12 Apr 2011 Comments
搭建本地YUM软件仓库 12 Apr 2011 Comments
用 repo 管理 Freemind 代码补丁 08 Apr 2011 Comments
开微博了 08 Apr 2011 Comments
RPM打包step by step(2) 08 Apr 2011 Comments
RPM打包step by step(1) 02 Apr 2011 Comments
搭建本地pypi服务器 14 Mar 2011 Comments
Topgit 安装 10 Mar 2011 Comments
《GotGit》附录D Git 和 Hg 面对面 10 Mar 2011 Comments
《GotGit》附录C Git 和 SVN 面对面 10 Mar 2011 Comments
《GotGit》附录B Git 与 CVS 面对面 10 Mar 2011 Comments
《GotGit》附录A Git 命令索引 10 Mar 2011 Comments
使用Buildout构建Python 25 Feb 2011 Comments
《Got Git》完稿 24 Feb 2011 Comments
整合Plone和Apache 21 Jan 2011 Comments
管理Plone的内容 14 Jan 2011 Comments
ZMI中使用portal_workflow管理工作流 07 Jan 2011 Comments
使用Plone来管理用户组 31 Dec 2010 Comments
Windows下Git的安装和配置 31 Dec 2010 Comments
设置启动Plone策略支持 31 Dec 2010 Comments
Plone的工作流 31 Dec 2010 Comments
回到未来 (3) 28 Dec 2010 Comments
回到未来 (2) 28 Dec 2010 Comments
回到未来 (1) 28 Dec 2010 Comments
Plone中管理用户和权限 27 Dec 2010 Comments
Plone产品实例 17 Dec 2010 Comments
Plone安装笔记 13 Dec 2010 Comments
buildout使用小例 10 Dec 2010 Comments
Vim 复制粘贴探秘 08 Dec 2010 Comments
python egg学习笔记 08 Dec 2010 Comments
Zope Dev Guide中产品例子学习 03 Dec 2010 Comments
Git 工作区、暂存区和版本库 30 Nov 2010 Comments
创建Zope产品例子 26 Nov 2010 Comments
补丁中的二进制文件 24 Nov 2010 Comments
Ubuntu10.10安装Zope小记 19 Nov 2010 Comments
Redmine 和 subversion 版本库整合的问题 17 Nov 2010 Comments
群英汇邮件列表更新 12 Nov 2010 Comments
Gerrit 代码审核服务器的工作流和原理 10 Nov 2010 Comments
Redmine 和 Subversion主备模式的整合 08 Nov 2010 Comments
Git 和 SVN 协同模型 04 Nov 2010 Comments
Redmine 中的 Subversion 版本库设置 01 Nov 2010 Comments
Git 的子树合并和子树拆分 31 Oct 2010 Comments
Topgit 原理及安装 28 Oct 2010 Comments
脱离 Gerrit 审核服务器,使用 repo 25 Oct 2010 Comments
看到了 Twitter 上鲁宾对乔布斯简短的回复 20 Oct 2010 Comments
关于限制低版本 Subversion 写操作问题的回复 20 Oct 2010 Comments
Android 代码工作区转换为 Android 的Git库镜像 15 Oct 2010 Comments
关于 Topgit 用法的回复 11 Oct 2010 Comments
Subversion 镜像写代理的配置注意事项 25 Sep 2010 Comments
Subversion 管理后台升级 25 Sep 2010 Comments
群英汇博客的里程碑 21 Sep 2010 Comments
Redmine 邮件发送问题的诊断 17 Sep 2010 Comments
用 Gitolite 搭建 Git 服务器 15 Sep 2010 Comments
关于 pySvnManager 的回复 15 Sep 2010 Comments
我给中央领导留言 13 Sep 2010 Comments
UNetbootin 让Linux安装更简单 09 Sep 2010 Comments
Android repo 魔法 31 Aug 2010 Comments
Repo——另一个Git协同模型 31 Aug 2010 Comments
Subversion管理后台增加对SVN容灾的支持 29 Aug 2010 Comments
维基升级——搜索功能改进 29 Aug 2010 Comments
Redmine 计划任务增加“未来”的选项 29 Aug 2010 Comments
Gistore 备份回滚改用分支实现 21 Aug 2010 Comments
爱上Git——《Git培训讲义》摘录 17 Aug 2010 Comments
如何同步 Gistore 的备份数据? 14 Aug 2010 Comments
晒晒我的计划任务 10 Aug 2010 Comments
Redmine任务日程安排(类似Mylyn)的功能 09 Aug 2010 Comments
Redmine与Mylyn的整合 09 Aug 2010 Comments
pySvnManager 0.5 升级指引 09 Aug 2010 Comments
pySvnManager 新功能:LDAP用户同步 08 Aug 2010 Comments
Xapian的检索 08 Aug 2010 Comments
解决 gistore 备份数据中的 git 库(submodule)的备份 03 Aug 2010 Comments
xapian索引的term处理 31 Jul 2010 Comments
nVidia 显卡在 Debian sarge 最新 linux 内核中的驱动 30 Jul 2010 Comments
Gistore(基于 git 的数据备份软件)升级至 0.2.x 29 Jul 2010 Comments
reST 输出文档中页眉和页脚的定制 28 Jul 2010 Comments
rst2pdf图片处理(续) 26 Jul 2010 Comments
群英汇redmine增强版ossxp-3.0成功上线 20 Jul 2010 Comments
Git 服务器软件 gitosis 的改进 20 Jul 2010 Comments
Redmine关于敏捷Scrum的插件 19 Jul 2010 Comments
为 TestLink 增加从 LDAP 同步用户的功能 19 Jul 2010 Comments
rst2pdf中图片的处理 19 Jul 2010 Comments
如果绿坝开源怎么样 13 Jul 2010 Comments
Debian 文件偷换 09 Jul 2010 Comments
在 reST 格式文档中,嵌入 Creative Commons 授权信息 09 Jul 2010 Comments
小心:谷歌 gmail 邮件过滤器 09 Jul 2010 Comments
redmine版本库统计 SVG 柱状图在IE中不能显示 02 Jul 2010 Comments
ruby中的代码块(Code Blokcs) 01 Jul 2010 Comments
安全 FTP 协议 FTPS 和防火墙 30 Jun 2010 Comments
ruby中的实例方法、类方法、单体方法、私有方法、protected方法 29 Jun 2010 Comments
LDAP 作为 FTP 认证源 29 Jun 2010 Comments
用 greylist 做邮件服务器的保护罩 28 Jun 2010 Comments
logcheck, fail2ban 的副作用 28 Jun 2010 Comments
“当时我就震惊了”——六月北京 OpenParty 20 Jun 2010 Comments
topgit 分支的图形化显示 18 Jun 2010 Comments
性能测试工具小黑马—JMeter+Badboy 13 Jun 2010 Comments
如何用apache+mongrel部署Rails应用 13 Jun 2010 Comments
pySvnManager 升级 11 Jun 2010 Comments
如何用nginx+mongrel部署Rails应用 09 Jun 2010 Comments
如何用nginx+passenger署Rails 09 Jun 2010 Comments
redmine 用户导入插件 08 Jun 2010 Comments
redmine 问题导入插件 08 Jun 2010 Comments
redmine同步变更集属性插件 07 Jun 2010 Comments
联通有点玩过火了 06 Jun 2010 Comments
改变 Nutch 对 robots.txt 的解析实现 03 Jun 2010 Comments
786个号中取203个,5连号的概率是多少? 03 Jun 2010 Comments
Python setuptools hack: get revision from git-svn 02 Jun 2010 Comments
Pylons nightmare ends? 01 Jun 2010 Comments
单点登录和 OpenID 31 May 2010 Comments
Subversion 用户眼中的 Git (13): Git 成为 SVN 的伙伴? 30 May 2010 Comments
Subversion 用户眼中的 Git (12): Git 有属性么? 30 May 2010 Comments
群英汇 Mailman 人性化设计(6): 存档安全性 30 May 2010 Comments
群英汇 Mailman 人性化设计(5): 创建新列表 29 May 2010 Comments
群英汇 Mailman 人性化设计(4): 列表订阅策略 27 May 2010 Comments
我在 OpenParty 上的报告 24 May 2010 Comments
TortoiseSVN自动获取redmine问题列表插件—TortoiseSVNRedmineIssuesPlugin 20 May 2010 Comments
Subversion 用户眼中的 Git (11): Git 授权没有 SVN 那样精细 17 May 2010 Comments
Check — 强大的c语言单元测试框架 17 May 2010 Comments
北京五月柳燕隙阳 17 May 2010 Comments
群英汇 Mailman 人性化设计(3): 列表订阅的人性化设计 17 May 2010 Comments
国内难觅稳定的 Debian 源 11 May 2010 Comments
群英汇 Mailman 人性化设计(2): 管理员认证方式和管理员面板的变化 09 May 2010 Comments
Subversion 用户眼中的 Git (10): Git 命令行的人性化设计 09 May 2010 Comments
从 CoSign 看开源软件本地化(7) 09 May 2010 Comments
NotesForLightBox灯箱控件示例 08 May 2010 Comments
12种常见的lightbox灯箱效果脚本 08 May 2010 Comments
Subversion 用户眼中的 Git (9): 单亲 VS 多亲 01 May 2010 Comments
群英汇 Mailman 人性化设计(1): 列表一览页增加登录和已订阅列表加亮 01 May 2010 Comments
从 CoSign 看开源软件本地化(6) 01 May 2010 Comments
SVN 树冲突和目录丢失问题(4) 23 Apr 2010 Comments
SVN 树冲突和目录丢失问题(3) 23 Apr 2010 Comments
SVN 树冲突和目录丢失问题(2) 22 Apr 2010 Comments
SVN 树冲突和目录丢失问题(1) 22 Apr 2010 Comments
Windows下安装Redmine的常见问题 20 Apr 2010 Comments
Windows 下Redmine-0.9.x的安装 20 Apr 2010 Comments
Ubuntu:不要迷恋哥,哥也只是一个传说 15 Apr 2010 Comments
从 CoSign 看开源软件本地化(5) 15 Apr 2010 Comments
Apt 级联缓存以及 apt-cacher-ng 的一个 Bug 13 Apr 2010 Comments
从 CoSign 看开源软件本地化(4) 13 Apr 2010 Comments
群英汇redmine增强版ossxp-2.0成功上线 11 Apr 2010 Comments
从 CoSign 看开源软件本地化(3) 11 Apr 2010 Comments
Redmine中文参考手册 09 Apr 2010 Comments
禁用 SSH 远程主机的公钥检查 08 Apr 2010 Comments
从 CoSign 看开源软件本地化(2) 08 Apr 2010 Comments
从 CoSign 看开源软件本地化(1) 07 Apr 2010 Comments
Fail2ban—-暴力口令破解的克星 07 Apr 2010 Comments
autotools系列工具—-自动生成Makefile 07 Apr 2010 Comments
单点登录认证系统升级 07 Apr 2010 Comments
Linux下C语言的调试 05 Apr 2010 Comments
提高Nutch局域网抓取的速度 31 Mar 2010 Comments
如何让 jQuery 和 prototype 共存 28 Mar 2010 Comments
Firebug—-javascript调试利器 26 Mar 2010 Comments
Redmine与TestLink的整合 25 Mar 2010 Comments
PHP中文乱码的处理 20 Mar 2010 Comments
Nutch 深度的测试 19 Mar 2010 Comments
群英汇redmine增强版ossxp-2.0已经冻结 17 Mar 2010 Comments
单点登录架构升级手册 16 Mar 2010 Comments
CoSign 3.x 介绍及与 CoSign 2.x 的协议比较 16 Mar 2010 Comments
CoSign 2.x 协议介绍 16 Mar 2010 Comments
单点登录版本升级:CoSign 3.x 更安全 16 Mar 2010 Comments
面向 PHP 5.3 友好的 PHP 开发 14 Mar 2010 Comments
TopGit的使用技巧 (3) 10 Mar 2010 Comments
TopGit的使用技巧 (2) 10 Mar 2010 Comments
TopGit的使用技巧 (1) 10 Mar 2010 Comments
群英汇部分应用的 /etc/init.d/ 下脚本名称改变 09 Mar 2010 Comments
Apache 性能调校 09 Mar 2010 Comments
软RAID 提供低成本和高可靠性的 Linux 服务器 09 Mar 2010 Comments
Linux 应用程序失去输入焦点问题的解决 09 Mar 2010 Comments
robots.txt 文件的非标准扩展 09 Mar 2010 Comments
Debian/Linux下Redmine的安装步骤 09 Mar 2010 Comments
关于机器人 /robots.txt 文件的常识 08 Mar 2010 Comments
Nutch的安装与配置 05 Mar 2010 Comments
Rails与Sphinx的整合 04 Mar 2010 Comments
Debian/Linux下Sphinx-for-chinese (中文全文搜索)的安装 04 Mar 2010 Comments
敏捷的MVC Web框架 Rails 04 Mar 2010 Comments
用 Rails 2.x.x 和 MySQL 搭建一个Web项目的步骤 04 Mar 2010 Comments
Debian/Linux上的txt文本文件拷到Mp3后变成乱码 01 Mar 2010 Comments
Debian 中的电子书 26 Feb 2010 Comments
Debian/Linux下sphinx的安装 26 Feb 2010 Comments
网站维护通知 24 Feb 2010 Comments
Python的易混地带 23 Feb 2010 Comments
Debian启动中的错误信息源自删除软件包遗留的配置文件,哦哈 22 Feb 2010 Comments
Python技巧篇(2):工具模块 22 Feb 2010 Comments
Debian 版本升级/降级 22 Feb 2010 Comments
Python技巧篇(1):内置对象及函数 22 Feb 2010 Comments
Subversion 用户眼中的 Git (8): SVN没有后悔药,git有好多 22 Feb 2010 Comments
Python的特点 21 Feb 2010 Comments
Ruby的特点 21 Feb 2010 Comments
Ruby的优点 21 Feb 2010 Comments
Python的优点 21 Feb 2010 Comments
Subversion 用户眼中的 Git (7): 完全不同的分支和里程碑的实现 21 Feb 2010 Comments
将博客整合到维基 09 Feb 2010 Comments
Subversion 用户眼中的 Git (6): stage 09 Feb 2010 Comments
jQuery 跨域 AJAX 07 Feb 2010 Comments
版本库整理的内存溢出问题 04 Feb 2010 Comments
用过滤器解决getRemoteUser()为的null的问题 04 Feb 2010 Comments
jquery和php整合实例 04 Feb 2010 Comments
解决getRemoteUser()为null的问题 02 Feb 2010 Comments
预告:新网站 ossxp.net 筹备中… 01 Feb 2010 Comments
Subversion 用户眼中的 Git (5): 没有部分检出 01 Feb 2010 Comments
Subversion 版本库整理实战 27 Jan 2010 Comments
删除 git submodule (git 库子模组) 26 Jan 2010 Comments
TestLink简明配置手册 25 Jan 2010 Comments
Subversion 用户眼中的 Git (4): 全局版本号和全球版本号 25 Jan 2010 Comments
Gistore 项目正式发布 —— 基于 Git 的 Linux 数据备份系统 23 Jan 2010 Comments
剥离CruiseControl dashboard控制台到Debian安装的tomcat6上 23 Jan 2010 Comments
Dashboard不能运行在Debian包安装的Tomcat6上? 23 Jan 2010 Comments
群英汇 TopGit 改进 (5): tg summary 执行的更快 23 Jan 2010 Comments
redmine 配置LDAP认证 22 Jan 2010 Comments
redmine 邮件服务的配置 22 Jan 2010 Comments
CruiseControl HgVersionParser: no match of 22 Jan 2010 Comments
群英汇 TopGit 改进 (4): tg 命令补齐 22 Jan 2010 Comments
维基中 Include 宏的用法 21 Jan 2010 Comments
用Debian/Ubuntu提供的软件包整合apache2和tomcat6 20 Jan 2010 Comments
Linux下Apache与Tomcat的整合 19 Jan 2010 Comments
Subversion 用户眼中的 Git (3): 命令集不兼容 19 Jan 2010 Comments
群英汇 TopGit 改进 (3): 更灵活的 tg patch 19 Jan 2010 Comments
预告: Gistore 项目开发中——使用Git做数据备份 18 Jan 2010 Comments
群英汇 TopGit 改进 (2): tg 导出全部分支 18 Jan 2010 Comments
Linux 下如何同时访问多个 github 帐号 17 Jan 2010 Comments
群英汇 TopGit 改进 (1): tg push 全部分支 15 Jan 2010 Comments
Subversion 用户眼中的 Git (2): 版本库, 工作区如影随形 15 Jan 2010 Comments
wordpress日志评论时间错误问题 14 Jan 2010 Comments
Subversion 用户眼中的 Git (1): 集中式 vs 分布式 14 Jan 2010 Comments
如何剥离CruiseControl内置的Web控制台 14 Jan 2010 Comments
velocity 未列入文档的秘密 13 Jan 2010 Comments
Git 如何拆除核弹起爆码,以及 topgit 0.7到0.8的变迁 13 Jan 2010 Comments
Wordpress中文昵称问题解决方法小结 12 Jan 2010 Comments
CruiseControl—confilg.xml 12 Jan 2010 Comments
wordpress工作原理 12 Jan 2010 Comments
能否突破 reST 表格语法的局限 11 Jan 2010 Comments
“团队致胜之道”文档更新——增加持续集成和测试管理平台相关内容 11 Jan 2010 Comments
reST编号列表的语法注意事项 10 Jan 2010 Comments
Subversion 1.6 修改记录 09 Jan 2010 Comments
Rails多语言支持 09 Jan 2010 Comments
TSE 表情插件的改进:错误表情替换的解决方案 09 Jan 2010 Comments
localization插件实现Rails多语言支持 09 Jan 2010 Comments
TestLink 1.8.5的完全安装 09 Jan 2010 Comments
版本库转换:hg->git->svn->git 08 Jan 2010 Comments
群英汇版本控制系统的选择:subversion, hg, git 07 Jan 2010 Comments
CruiseControl—下载及安装 07 Jan 2010 Comments
CruiseControl —初体验 07 Jan 2010 Comments
博客软件的选型:typo 还是 wordpress 06 Jan 2010 Comments
TestLink-测试管理工具 06 Jan 2010 Comments
redmine-项目管理工具 06 Jan 2010 Comments
CoSign-SSO插件已提交到Subversion版本库及github 06 Jan 2010 Comments
WordPress插件编程资源列表 06 Jan 2010 Comments
2010年群英汇第一大事——开博 05 Jan 2010 Comments