Subscribe最新博客(更多

2014-04-24

使用 git-svn 和 git-filter-branch 整理 SVN 版本库

SVN 本身提供了如下版本库整理工具:

  • svnadmin dump
  • svndumpfilter include
  • svndumpfilter exclude
  • svnadmin load

其中 svnadmin dump 将整个版本库或部分提交导出为一个导出文件; svndumpfilter 基于配置项的路径(SVN 1.7的 svndumpfilter 还支持通配符路径)对导出文件进行过滤, 过滤结果保存为新的导出文件; svnadmin load 将导出文件导入到另外的版本库中, 导入过程有两个选择——维持路径不变,或导入到某个路径之下。

相对于Git提供的用于整理提交的 git filter-branch 命令,SVN的版本库整理工具能做的实在不多。 而且SVN的相关工具容错性太差,操作过程经常被中断,可谓步步惊心。

最近遇到的一个案例,需要将两个 SVN 版本库(bar 和 baz)的全部历史导入到另外一个 SVN 版本库(foo)中。 并要求版本库 bar 和 baz 的目录结构统一采用 foo 中规定的目录结构。面对要导入的近 20GB 数据(绝大部分是Word、Excel、PDF文档), 决定采用Git提供的工具集进行SVN版本库整理。整理过程和过程中开发的脚本记录如下。

将 bar 和 baz 版本库转换为本地Git库

以 bar 为例,将两个版本库(bar 和 baz)转换为本地的 Git 版本库,以便使用强大的 git filter-branch 命令对提交逐一进行修改(如修改版本库中的文件路径)。

$ git init git/bar
$ cd git/bar
$ git svn init --no-metadata file:///path/to/svn/bar
$ git svn fetch

说明:

  • SVN 版本库 bar 位于本机的路径 /path/to/svn/bar 下。
  • 导出的 Git 版本库位于 git/bar 目录下。
  • 因为版本库 bar 并未使用分支(未采用 trunk、branches、tags目录结构),因此执行 git svn 时并未使用 -s 等参数。

源版本库中文件名过长的问题

Windows和Linux下文件名长度限制不同,前者255个Unicode字符,后者为255个字节。 在此次转换中就遇到 bar 版本库中存在若干文件名超长的文件,导致无法在 Linux 平台上检出。 为避免后续操作中出现错误,对其进行重命名。

首先创建一个脚本 rename.sh,该脚本将提供给 git filter-branch 命令对版本库中超长文件名进行重命名操作。

#!/bin/sh

git ls-files -s | \
sed \
    -e "s#\(\t.*/file-name-is-too-long\).*\.pdf#\1-blahblah.pdf#"  \
| GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"

然后执行下面命令对版本库整理:

$ cd git/bar
$ git filter-branch --index-filter 'sh /path/to/rename.sh'

删除空白提交

从SVN转换的Git版本库可能存在空白提交,例如一些仅修改了SVN属性的提交不被 git-svn 支持,转换成了空提交。 这些空提交会对后续操作造成干扰,执行如下命令删除空白提交:

$ cd git/bar
$ git  filter-branch -f --commit-filter '
  if [ "$(git rev-parse $GIT_COMMIT^^{tree} 2>/dev/null)" = "$(git rev-parse $GIT_COMMIT^{tree})" ];
  then
      skip_commit "$@";
  else
      git commit-tree "$@";
  fi' HEAD

向Git日志中添加MetaData

执行 git log 操作可以看到转换后的提交保持了原有SVN提交的用户名和提交时间,还记录了对应SVN的提交编号信息。 但是后续操作(git svn dcommit)会改变Git提交,破坏其中包含的原有SVN提交的提交者和提交时间, 因此需要用其他方法将这些信息记录下来,以便补救。

使用 git filter-branch--msg-filter 过滤器逐一向提交插入原有SVN的提交者和提交时间的元信息。

$ cd git/bar
$ git filter-branch -f --msg-filter '
  cat &&
  echo "From: REPO-NAME, author: $GIT_AUTHOR_NAME, date: $GIT_AUTHOR_DATE"' HEAD

根据需要对版本库目录重新组织

git filter-branch 至少有两个过滤器可以对提交中的目录和文件进行组织。一个是 --tree-filter , 一个是 --index-filter 。前者的过滤器脚本写起来简单,但执行起来较后者慢至少一个数量级。

根据路径转换的需求,编写过滤器脚本,如脚本 transform.sh

#!/bin/sh

if test -z "$GIT_INDEX_FILE"; then
    GIT_INDEX_FILE=.git/index
fi

git ls-files -s | \
sed \
    -e "s#\(\t\)#\1new-root/#"  \
    -e "s#\(\tnew-root\)\(/old-path-1/\)#\1/new-path-1/#" \
    -e "s#\(\tnew-root\)\(/old-path-2/\)#\1/new-path-2/#" \
    -e "s#\(\tnew-root\)\(/old-path-3/\)#\1/new-path-3/#" \
| GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"

然后执行如下命令对提交进行逐一过滤,将老的目录结构转换为新的目录结构:

$ cd git/bar
$ git filter-branch --index-filter 'sh /path/to/transform.sh'

用git-svn克隆目标版本库(foo)

执行如下命令将导入的目标版本库转换为本地的 Git 版本库,如下:

$ git init git/foo
$ cd git/foo
$ git svn init --no-metadata file:///path/to/svn/foo
$ git svn fetch

然后将 bar 整理好的分支变基到当前的 master 分支上:

$ cd git/foo
$ git fetch ../../git/bar
$ git branch bar/master FETCH_HEAD
$ git co bar/master
$ git rebase -k --onto master --root

说明:

  • 使用 -k 参数,执行效率更高,因为会直接调用 cherry-pick 进行变基,而不需要执行 git format-patch 命令将提交预先转换为补丁文件。

在执行过程中遇到冲突中断的情况,这时需要解决冲突后执行:

$ git cherry-pick --continue

然后执行如下命令将不在SVN版本库中的Git提交提交到SVN版本库 foo 中。

$ git svn dcommit --rmdir 

说明:

  • 使用 --rmdir 命令是为了避免在 SVN 版本库中残留由于目录移动产生的空目录。
  • 使用 git svn dcommit 在SVN版本库中创建的新提交,其提交者是当前登录用户,提交时间是当前时间。 即新的SVN提交丢失了原有SVN提交的用户名和时间信息。马上利用之前在提交说明中添加的元信息进行补救。

修正提交时间和提交者

编写如下脚本 parse-git-log.rb,读取Git日志对元信息进行处理。

#!/usr/bin/ruby

require 'date'

def to_iso8601(date)
    if date =~ /^[0-9]{10}/
      DateTime.strptime(date, '%s').iso8601.gsub(/\+[0-9]*:[0-9]*$/, '.000000Z')
    else
      raise "Error: wrong date format: #{date}"
    end
end

def parse_git_log(io)
  svndict={}
  commit, author, date, log, rev = []
  io.each_line do |line|
    line.strip!
    if line =~ /^commit ([0-9a-f]{40})/
      commit = $1
      author, date, log, rev = []
    elsif line =~ /^From: .*, author: (.*), date: @([0-9]+)/
      author = $1
      date = $2
    elsif line =~ /git-svn-id: .+@([0-9]+) .*/
      rev = $1
      if author.nil? or author.empty?
        STDERR.puts "Warning: no author for commit: #{commit}"
        next
      elsif date.nil? or date.empty?
        STDERR.puts "Warning: no author for commit: #{commit}"
        next
      end
      svndict[rev] = {}
      svndict[rev][:author] = author
      svndict[rev][:date] = to_iso8601 date
    end
  end
  svndict
end

url = 'file:///path/to/svn/foo'
svndict = {}

if ARGV.size == 1
  if File.exist? ARGV[0]
    File.open(ARGV[0]) do |io|
      svndict = parse_git_log io
    end
  else
    STDERR.puts "Read git log from STDIN"
    url = ARGV[0]
    svndict = parse_git_log STDIN
  end
else
  puts <<-EOF
  Usage:
      #{File.basename $0} git-log.txt
      #{File.basename $0} url-of-svn < git-log.txt
  EOF
  exit 0
end

svndict.keys.map{|x| x.to_i}.sort.reverse.each do |rev|
  author = svndict[rev.to_s][:author]
  date = svndict[rev.to_s][:date]
  puts "svn ps --revprop -r #{rev} svn:date   \"#{date}\" #{url}"
  puts "svn ps --revprop -r #{rev} svn:author \"#{author}\" #{url}"
end

然后执行如下命令,读取Git日志,将Git提交中的元信息转换为修正 SVN 提交历史的命令脚本 fix-svn-log.sh

$ cd git/foo
$ git log | ruby parse-git-log.rb file:///path/to/svn/foo > fix-svn-log.sh

然后执行如下命令修改 SVN 的属性,还原原有SVN的提交用户和提交实现信息:

$ sh fix-svn-log.sh

因为此操作实际上执行 svn ps --revprop 命令,需要SVN版本库 foo 中创建一个可执行的 pre-revprop-change 钩子脚本。

至此版本库转换完毕。怎么样 git filter-branch 命令够强大吧。

View Comments
2013-10-26

复用 git.git 测试框架

Git 项目(git.git)有着别具一格的测试框架,使用 shell 脚本开发测试用例, 写起测试用例来一点都感觉不到拖泥带水,就和在 shell 环境中手工测试一样。 最近在重构 Gistore 项目时复用了这一 Git 项目特有的测试框架,对 Gistore 进行测试。愿这一测试框架可以被更多的项目借鉴。

git.git 的测试框架

Git 项目主要采用了 C 语言,同时还包含了 Perl、Shell 等多种开发语言的项目。 Git 项目的测试并没有采用常见的类似 JUnit 测试框架,而是采用自创的测试框架, 由 Junio Hamano 在 2005 年用 shell 脚本封装而成。在这个框架下, 写测试用例和测试套件自然也是使用 shell 脚本语言,写起测试用例来就和手工在 shell 环境下针对命令行测试没什么两样,写测试用例的过程很是“享受”。还一个原因可能是 shell 脚本语言几乎融入了每一个 *nix 开发者的血液中。总之这个测试框架用起来非常顺手。

在 Git 项目的 t/ 目录下存在成百上千个以 "t<四位数字>-<测试套件名称>.sh" 格式命名的文件。每一个 Shell 脚本文件即是一个测试套件,其中包含多个测试用例。

若打开这些 shell 脚本,会注意到每一个测试套件(t<四位数字>-<套件名>.sh)都包含相似的结构。

# 引入测试套件函数库
. ./test-lib.sh

# 定义和执行一个测试用例
test_expect_success '<测试用例名称>' '
        <测试断言1> &&
        <测试断言2> &&
        ...
        <测试断言n>
'

# 此处省略更多的测试用例
test_expect_success ...

...

# 声明测试套件结束,并对测试执行过程为测试套件生成的临时目录进行清理
test_done

这些 shell 脚本(测试套件)都可以单独运行。例如下面示例中执行的测试套件就是我为 git-clean--interactive (交互式 git clean)写的测试套件。

$ sh t7301-clean-interactive.sh
ok 1 - setup
ok 2 - git clean -i (c: clean hotkey)
ok 3 - git clean -i (cl: clean prefix)
ok 4 - git clean -i (quit)
ok 5 - git clean -i (Ctrl+D)
ok 6 - git clean -id (filter all)
ok 7 - git clean -id (filter patterns)
ok 8 - git clean -id (filter patterns 2)
ok 9 - git clean -id (select - all)
ok 10 - git clean -id (select - none)
ok 11 - git clean -id (select - number)
ok 12 - git clean -id (select - number 2)
ok 13 - git clean -id (select - number 3)
ok 14 - git clean -id (select - filenames)
ok 15 - git clean -id (select - range)
ok 16 - git clean -id (select - range 2)
ok 17 - git clean -id (inverse select)
ok 18 - git clean -id (ask)
ok 19 - git clean -id (ask - Ctrl+D)
ok 20 - git clean -id with prefix and path (filter)
ok 21 - git clean -id with prefix and path (select by name)
ok 22 - git clean -id with prefix and path (ask)
# passed all 22 test(s)
1..22

运行测试套件的输出结果(显示到标准输出的内容)是经过特别设计的。成功运行的测试用例显示为:

ok <数字> - <测试用例名>

而运行失败的测试用例会显示为:

not ok <数字> - <测试用例名>

在测试套件运行的结尾会显示如下统计信息:

<数字>..<数字>

这种特定的输出格式被称为 TAP (Test Anything Protocol),参见 http://testanything.org/

Junio 还用 shell 脚本封装了一个测试夹具(test harness),在 t/ 目录下,直接执行 make 命令即可执行全部的测试套件,并对测试结果进行统计。此外还有其他的测试夹具可供使用, 例如名为 prove 的命令可以多进程并发地执行测试套件,让测试过程更高效。

$ prove --timer --jobs 15 ./t[0-9]*.sh
[19:17:33] ./t0005-signals.sh ................................... ok       36 ms
[19:17:33] ./t0022-crlf-rename.sh ............................... ok       69 ms
[19:17:33] ./t0024-crlf-archive.sh .............................. ok      154 ms
[19:17:33] ./t0004-unwritable.sh ................................ ok      289 ms
[19:17:33] ./t0002-gitfile.sh ................................... ok      480 ms
===(     102;0  25/?  6/?  5/?  16/?  1/?  4/?  2/?  1/?  3/?  1... )===

测试 Gistore

Gistore 是我在2010年写的一个工具, 以 Git 作为后端存储实现对磁盘文件的备份,并作为独立的一章写到了《Git权威指南》 一书中。

Gistore = Git + Store

最近用 Ruby 语言重写了 Gistore。这是因为 Gistore 最初的设计依赖 mount 命令, 需要将备份目录挂载到临时工作区,故只能用于有限的平台上,且可能需要 root 用户权限。 考虑到 Git 的 gitignore 语法增加了对双星号(**)通配符的支持,是不是用 gitignore 机制实现 Gistore 更好呢?改用 Ruby 实现是因为最近几年 Ruby 用得多, 而且使用 Thor (一个实现命令行编程框架的 Ruby 包,被很多著名软件如 bundle、rails 等使用)可以更容易实现工具的命令行扩展。

软件重构的质量需要测试用例来保证。Ruby 虽然内置了强大的测试框架,但像 Gistore 这类大量调用外部命令的应用,采用 Git 项目的测试框架可能更理想。于是在我 Gistore 项目中重用了 Git 项目的测试框架。

使用该测试框架的注意事项如下:

用 && 组合多个测试断言

下面的测试用例中,因为在第二句断言(false)后面丢掉了一个 && , 导致前两个断言未对测试用例施加影响。

#!/bin/sh
#

. ./test-lib.sh

test_expect_success 'test framework assertion' '
        true &&
        false
        true
'

test_done

用 test_cmp 断言测试输出

该测试框架中最常用到的断言除了 shell 本身包含的 test 命令外,就是 test_cmp 断言。 实际上 test_cmp 就是对 diff 命令的简单封装。具体的使用过程是先将预期结果写入文件 expect ,测试输出写入 actual 文件,再用 test_cmp 比较 expectactual 文件, 内容一致则成功,否则失败。例如下面的测试用例代码:

cat >expect << EOF
root/doc/COPYRIGHT
root/src/README.txt
root/src/images/test-binary-1.png
root/src/images/test-binary-2.png
root/src/lib/a/foo.c
root/src/lib/b/bar.o
root/src/lib/b/baz.a
EOF

test_expect_success 'initialize for commit' '
        prepare_work_tree &&
        gistore init --repo repo.git &&
        gistore add --repo repo.git root/src &&
        gistore add --repo repo.git root/doc &&
        gistore commit --repo repo.git &&
        test "$(count_git_commits repo.git)" = "1" &&
        gistore repo repo.git ls-tree --name-only \
                -r HEAD | sed -e "s#^${cwd#/}/##g" > actual &&
        test_cmp expect actual
'

用 test_must_fail 断言命令失败或异常

该测试框架中有两个看起来很像的方法 test_expect_failuretest_must_fail, 前一个函数类似于 test_expect_success,以命令参数的方式引入一个测试用例并进行测试。 后一个是用于测试用例中的测试断言。

函数 test_expect_failure 通过命令行参数引入的测试用例,无论执行成功与否, 测试都不会中断。测试用例执行失败会显示:

not ok 1 - test framework assertion # TODO known breakage
# still have 1 known breakage(s)

测试成功会显示:

ok 1 - test framework assertion # TODO known breakage vanished
# 1 known breakage(s) vanished; please update test(s)

函数 test_must_fail 作为测试断言,用于确认一个命令会以失败结束(返回非0值)。 例如下面测试用例用于测试对所有注册的备份任务执行备份时(即执行 gistore commit-all 命令时),如果有一个或多个 Gistore 备份任务的指向丢失时,其它备份任务的备份不会受到影响, 并且 gistore commit-all 命令运行结束后要返回非零值。

test_expect_success 'commit-all while missing task repo' '
        gistore task add hello repo1.git &&
        gistore task add world repo2.git &&
        test "$(count_git_commits repo1.git)" = "4" &&
        test "$(count_git_commits repo2.git)" = "3" &&
        do_hack &&
        gistore commit-all &&
        test "$(count_git_commits repo1.git)" = "5" &&
        test "$(count_git_commits repo2.git)" = "4" &&
        mv repo1.git repo1.git.moved &&
        do_hack &&
        test_must_fail gistore commit-all &&
        test "$(count_git_commits repo2.git)" = "5" &&
        mv repo1.git.moved repo1.git &&
        mv repo2.git repo2.git.moved &&
        test_must_fail gistore commit-all &&
        test "$(count_git_commits repo1.git)" = "6"
'

测试用例设置依赖条件按需运行

因为 Git 1.8.2 之后才为 gitignore 引入双星号(**)通配符,而之前版本的 Git 并不支持, 这会导致某些测试用例结果不一致。

Git项目的测试框架在设计之初就考虑到了这种情况,可以通过设置依赖条件在某些情况下关闭特定测试用例的运行。

首先在测试框架中根据执行环境的不同,预置特定的依赖条件,例如下面的代码使得当 Git 命令的版本是 1.8.2 或更新的版本时,预置 GIT_CAP_WILDMATCH 依赖条件。

if test $(gistore check-git-version 1.8.2) -ge 0; then
    test_set_prereq GIT_CAP_WILDMATCH
fi

然后在定义测试用例的 test_expect_success 的第一个参数中写入相应的依赖条件。 例如如下的测试用例只在 Git 1.8.2 以上环境下运行。

# Before git v1.7.4,  filenames in git-status are NOT quoted.
# So strip double quote before compare with this.
cat >expect << EOF
 M root/doc/COPYRIGHT
 M root/src/README.txt
 D root/src/images/test-binary-1.png
 D root/src/lib/b/baz.a
?? root/src/lib/a/foo.h
EOF

test_expect_success GIT_CAP_WILDMATCH 'status --git (1)' '
    gistore commit --repo repo.git && \
    echo "hack" >> root/doc/COPYRIGHT && \
    echo "hack" >> root/src/README.txt && \
    touch root/src/lib/a/foo.h && \
    rm root/src/images/test-binary-1.png && \
    rm root/src/lib/b/baz.a && \
    gistore status --repo repo.git --git -s \
        | sed -e "s#${cwd#/}/##g" | sed -e "s/\"//g" > actual &&
    test_cmp expect actual
'

进行函数级测试

Git项目的测试框架主要是进行集成测试,如果需要进行函数级测试,还需要下点功夫。 即需要对函数进行简单的命令行封装,用命令行调用的方式对函数进行测试。

在 Git 项目中就用代码 "test-path-utils.c" 对路径处理相关函数进行封装,在测试用例 t0060 中调用 test-path-utils 进行相关测试。

. ./test-lib.sh

relative_path() {
        expected=$(test-path-utils print_path "$3")
        test_expect_success $4 "relative path: $1 $2 => $3" \
        "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
}

relative_path /foo/a/b/c/       /foo/a/b/       c/
relative_path /foo/a/b/c/       /foo/a/b        c/
relative_path /foo/a//b//c/     ///foo/a/b//    c/              POSIX
relative_path /foo/a/b          /foo/a/b        ./
relative_path /foo/a/b/         /foo/a/b        ./
relative_path /foo/a            /foo/a/b        ../
relative_path /                 /foo/a/b/       ../../../

在 Gistore 项目中,我也用到了类似的方法。通过隐含子命令 check-git-versionGistore.git_version_compare 方法进行封装,并在测试用例 t0020 中进行针对性测试。

test_expect_success 'compare two versions' '
        test $(gistore check-git-version 1.8.5 1.8.5) -eq 0 &&
        test $(gistore check-git-version 1.8.4 1.8.4.1) -eq -1 &&
        test $(gistore check-git-version 1.7.5 1.7.11) -eq -1 &&
        test $(gistore check-git-version 1.7.11 1.7.5) -eq 1 &&
        test $(gistore check-git-version 1.7.11 1.7.5) -eq 1 &&
        test $(gistore check-git-version 1.7.11 2.0) -eq -1 &&
        test $(gistore check-git-version 2.0 1.8.5) -eq 1
'

更多测试用例的写法,参见如下链接:

插播小广告:

View Comments
2013-03-04

墙不住的Git官网

微博中关于《Git Community Book》(Git社区书)本地化的帖子,使我想起久未造访的 Git官网 , 却吃惊地发现Git官网已遭G\F\W认证。值此“两会”召开、古月三昷卸任之季,G\F\W居然再次做出如此这般非和谐之举, 不啻于继股市暴跌后又一记对“十年和谐”响亮的耳光。

《墙不住的Git官网》这篇文章送给那些仍在攒钱买房,尚无闲钱购置境外主机以搭建VPN、SSH,也不会使用 goagent 的码农。

Git官网 的维护者是 GitHub 的 Scott Chacon,他也是 Git社区书 的主要维护者和 《ProGit》 的作者。

新设计的Git官网不但重新设计了Git的Logo,还将《ProGit》这本书贡献到官网,取代Git社区书成为官网上的官方教程。 (毕竟一人维护两套书负担太重,而且不小心会被人误解为 Copy & Paste。)

Git官网的源代码托管在GitHub,已由 旧版网站地址 更换到新的地址:

新网站基于 Rails 构建,默认使用 sqlite 本地数据库。其中 HTML 格式的 Git 手册、ProGit电子书的源代码并不在此版本库中, 而是要执行相应的 rake 任务,从Git版本库和 ProGit版本库中获取内容、编译并保存到 sqlite 数据库中。 下面介绍一下如何在本地搭建Git官网。

准备

  • 克隆Git版本库(可选)

    Git官网中的Git手册直接从Git项目的本地版本库中编译,会对 Git v1.0 之后的每一正式发布版本的手册进行编译。

    如果本地已经克隆了Git版本库,可以跳过这一步。

    如果没有克隆Git版本库,先克隆一份Git版本库。

      $ cd Your/WorkSpace/
      $ git clone git://github.com/git/git
    
  • 克隆Git官网版本库

      $ cd Your/WorkSpace/
      $ git clone git://github.com/github/gitscm-next
    
  • 进入到克隆出的Git官网版本库

      $ cd gitscm-next
    
  • 安装 Ruby 1.9.2 。

    (在 gitscm-next 目录下的 .ruby-version 这个文件指定了 ruby 的版本。)

      $ rvm install 1.9.2
      $ rvm use 1.9.2
    
  • 下载并安装依赖的 Gem 包。

    (由文件 Gemfile 设定 gem 包依赖)

      $ bundle install
    

Rails 应用配置

Git新官网是一个 Rails 应用。数据库的默认配置文件 config/database.yml 已指定使用 sqlite3 本地数据库。 执行如下命令即可创建该本地数据库。

  • 初始化本地数据库。(执行数据迁移操作)。

      $ rake db:migrate
    
  • 导入缺省数据

      $ rake db:fixtures:load
    

第一次启动应用

  • 启动Web应用

      $ script/rails server
      ...
      => Rails 3.2.11 application starting in development on http://0.0.0.0:3000
      ...
    
  • 文档页面 404

    从上面命令的输出可以看出启动的内置Web服务器运行在 3000 端口。打开 Web 浏览器,输入地址 http://localhost:3000/ , 墙外的 Git官网在本地重现了。在网站中四处转转,会发现文档部分的链接( doc/ )催悲地404了。

    这是因为相关文档需要从其他版本库获取数据并编译。

  • 退出Web应用

    在控制台按下 Ctrl+C 退出运行在 3000 端口的 Web 服务。

编译 Git 手册

阅读版本库根目录下的 README.md 文件(GitHub上项目的说明文件),可以看到编译文档的说明。

  • 编译Git手册。

    如果你有耐心,可以执行下面的命令,将Git v1.0 之后的 240 多个正式发行版本的文档逐一编译(当然很多小版本并未更新文档),并保存到数据库中。

      $ GIT_REPO=/Your/WorkSpace/git/.git rake local_index
    

    你也可以只编译Git某一个版本的手册。如下:

      $ GIT_REPO=/Your/WorkSpace/git/.git REBUILD_DOC=v1.8.1 rake local_index
    

    (关于该 rake 命令的具体实现参见脚本: lib/tasks/local_index.rake

  • 访问编译的Git手册。

    再次启动Web应用,文档页面仍然显示 404 错误。编译的Git手册文档藏到哪里了呢?从 Rails 的路由文件 config/routes.rb 文件可以猜出Git手册页面的URL地址为:

    按 Ctrl+C 退出Web应用。

编译 ProGit 和其他文档

执行下面命令可以编译出其余文档,包括《ProGit》电子书。

  • 更新Git下载链接,执行如下命令:

      $ rake downloads
    
  • 编译 ProGit 电子书。

    提供您的 GitHub 账号——将如下命令中的 your_github_username 和 your_github_password 用您的用户名及口令替换。 执行 rake 命令,通过 GitHub API (调用 octokit 包)远程读取 progit 版本库源码,编译电子书。

      $ export API_USER=your_github_username
      $ export API_PASS=your_github_password
      $ rake remote_genbook
    

至此 Git官网在本地部署完毕,运行内置 Web server:

$ script/rails server
...
=> Rails 3.2.11 application starting in development on http://0.0.0.0:3000
...

墙外的Git官网在本地重现: http://localhost:3000/

View Comments

我的书

联系方式