1. Git是什么?
Git 是 Linux 的创始人 Linus Torvalds 开发的开源和免费的版本管理系统,也称源代码管理系统(Source Code Manage, SCM)。它的官方网站是 Http://git-scm.com。
2005 年的时候 Linux 核心开发小组和当时的他们的版本管理系统提供商产生了分歧。他们不能再使用原有的版本管理系统了。当时 Linus 环顾宇宙之内竟然没有一个能满足自己需求的版本管理系统可用。于是他毅然决定自己动手开发一个!这就是 Git。时至如今,Git 已经成为许多著名系统的版本管理系统。比如Linux 核心,Eclipse,Android,Ruby on Rails,PostgreSQL,jQuery …… http://en.wikipedia.org/wiki/Git_(software)
Git 在英文中原意是蠢货或者笨蛋。Linus 用了这么一个名字反映出他是个个性很强的人。他在2007年的 Google Tech Talk 会议上介绍 Git 的时候用了非常强悍的语言数落了一些当时著名的版本管理系统 CVS 和 Subversion。 全世界大概也就 Linus 够这个资格这么说话吧。http://www.youtube.com/watch?v=4XpnKHJAok8
我觉得 Git 是个很酷的名字。比起起来开源系统的名字,什么水银(Mercurial),什么集市(Bazaar)等等,Git 要响亮得多。当然这不是我喜欢 Git 的原因。我喜欢 Git 是在我使用 Git 一段时间后觉得它实在是好。具体怎么好法,我准备写个 Git 故事连载,一一记录下来和大家分享。
在网上已经有许多不错的介绍 Git 的资料。它们多是在 Linux 或者 Mac OS 下用命令行。看不到几页就会犯困。我的Git故事连载特点是全部是在 Windows 下使用 Git,而且尽可能多的图形界面 (GUI)。这点在前几年不大可能办到。最初Git 的开发者们对于 Windows 下运行的 Git 很不以为然。网上搜索引擎检索出来的结果有相当一部份都说Windows下不适合用 Git。各位要注意的是搜索引擎里过时的内容可不少。看网上的文章千万要注意它的发布日期。2010年以前的评价Windows下用Git的大都可以不用浪费时间去看。我的Git故事连载以后也会过时的,但是现在是最新鲜的。
2. 怎样安装 Git
与网上现有不少资料就事论事逐一介绍Git的功能和命令不同,这里主要讲Git用法背后要解决的问题,以及对这些问题的衍生思考。所以是讲故事,而不是给你罗列Git的好处。让你不仅知其然,而且知其所以然。我要讲的第一个集故事就是如何要把Git安装起来。
从 Git的官方主页上可以连接到 Git for Windows下载专区。这里有好几个文件。这里简单介绍一下:
- 以Git-开头的文件。它是Git for Windows的安装文件。它象大多数Windows程序那样把Git装到C:\Program Files 目录下,注册好Git程序,也能让你从控制面板中删除。
- 以PortableGit-开头的文件。所谓Portable就是我们常说的绿色版。释放出来就能使用,不需要修改系统文件或者修改注册表。
- 以msysGit-开头的文件。它们是完整的开发环境,包括了Git的源程序和GNU C编译器。它可以用来把Git的源程序编译成可执行程序。
一般用户应该用Git-开头的安装文件。但是先不要安装它,继续往下读。
在前文中提到,本故事连载主要讲在Windows下使用Git。好像一些耍酷的Linux用户只用命令行那样,我也可以耍一下酷,在Windows下我可以全部只用图形化界面。
Git Extensions 是Windows下的开源的Git图形界面。有了这个工具,所有Git在Windows下不好用的抱怨都过时了。它是用 Windows Forms 技术开发。里面有很多技术可以借鉴学习,也许将来可以来一集故事剖析剖析它,看看如何开发 Git的扩展工具。Git Extensions 的下载区(也有好几个文件。我们要用GitExtensionsXXXSetupComplete.msi。其中XXX是版本号。由于这个工具更新很快,版本号会不断变化。有Complete的这个安装文件包括了Git for Windows,Git Extensions 和 KDiff3。这是个三合一的安装程序。其中KDiff3是个必须的源程序合并工具,在以后的故事里你会看到它用处很大。
这一集的核心思想就是用Git Extensions是目前Windows下一个出色的Git图形界面。它的安装文件可以一次把三个工具都安装好。安装好后,建议阅读它的文档学会基本操作如何创建仓库(initialize repository),如何提交程序文件进仓库(commit)等等。
3. 与 Visual Studio 集成
工欲善,必先利其器。Windows 下用 Eclipse开发程序,Eclipse 的 EGit 插件提供了 Git 的支持。
如果用 Visual Studio,你一定要安装个 Git Source Code Control Provider 插件。方法是用菜单 Tools | Extensions Managers…,检索 git 然后下载安装。
安装以后,Git 管理的程序项目里文件的状态就会在 Visual Studio 里显示出来。
Git Extensions 的也被集成到右件菜单中。比如你可以直接提交(commit)。
当创建一个新项目或者打开一个现有项目,在项目浏览器用右键菜单可以快速建立一个 Git 仓库。非常方便。我已经为我所有程序项目都轻轻松松创建了 Git 仓库。
这样对于习惯于用 Source Safe 和 TFS 的用户,版本控制的感觉又回来了。Git 的功能调用起来也非常顺手。
最后随便提一下这个 Git Source Control Provider 是我在开发的一个开源项目。它的主页是 http://gitscc.codeplex.com。欢迎参与和指教。
4. 乌龟工具
Windows下除了Git Extensions以外,外有个很好用的图形工具TortoiseGit。它是著名的乌龟工具组里的一员。它的同门弟兄个个大名鼎鼎。它们是TortoiseCVS,TortoiseSvn,TortoiseHg,TortoiseBzr等等。
TortoiseGit的官方主页是 http://code.google.com/p/tortoisegit。它是Windows文件管理器的外挂。它为文件管理器右键菜单增加了Git相关的功能菜单。并且可以在文件管理器里显示文件和目录在Git下的状态 (这点其实我并不喜欢。我不想让它把我文件夹里文件的图标弄得乱糟糟的)。不过对于用过它同类工具的人来说就会感觉很熟悉。
最终我还是把它加到了 Visual Studio 的 Git Source Control Provider 插件里。主要原因是它启动起来比Git Extensions快。
对比Git Extensions 和 TortoiseGit 的提交(commit) 命令窗口。Git Extensions 可以预览文件的改动(Diff),可以控制Git的Stage。当时正是这个Stage功能需要你一个额外的步骤,就是必须要把文件加到Stage,然后才能提交。很多情况下我发现用TortoiseGit来得快。一是它跳出来得快,再者是它帮你完成了加文件到stage,省掉了你一个步骤。
我建议读者可以尝试一下TortoiseGit。当然如果你曾经用过乌龟工具组的工具,那你会很习惯于它们一致的风格和操作方法。
5. 管理程序的历史
在程序开发的过程中,程序是一步步不断在演变中的。新的功能不断加入,错误的地方不断被修正。这每一步都包含很多有用信息。源程序管理系统 (Source Code Management, SCM) 能帮助我们保留和管理这些信息。保留和管理历史信息是软件/程序版本控制 (Version Control/Revision Control) 的前提。
当程序开发进行到一个阶段,我们通知 Git 请你记录下我这一步。这个动作称为提交 (commit, verb),提交的内容也称提交 (commit, noun)。有趣的是英文和中文里提交 (commit) 这个词都可以被用做动词和名词。中文翻译成提交我不是很满意,但是目前没有好的词。
Git 的提交往往一组文件,而不单个文件一个提交。它的好处是使得每个提交成为一个单元,比较容易管理。比如一个提交改了数个文件,如果你要取消这个提交,Git 自动处理所有文件。你无需一个个文件去挑出来。除了老式的 CVS 和Source Safe,现在比较新的源程序管理系统都是一样提交一批文件。Subversion 也用提交 (commit) 同时作为提交动作和提交的内容。而Mercurial 提交动作用 commit,提交的内容则叫变化集 (change set)。
当我们保存一些编程的步骤后,Windows下就可以用 Git Extensions 的浏览 (browse) 屏幕回顾和查看程序的历史了。
屏幕的上半是程序历史上的所有步骤,也称提交(commit)的列表。屏幕的下半是选中的提交的具体信息。它分成三页分别来显示提交的详细内容,提交时完整的文件结构(file tree),以及与上次提交之间的差别(diff)。用颜色来显示文件的差别(diff)很容易阅读。对应的Git命令是git log和git diff。如果用命令行可就没有图形界面这么直观了。
记录程序的历史是任何一个版本控制软件都能做的基本功。CVS,Source Safe,Subversion,TFS(Microsoft Team Foundation Server),Mercurial 无一例外。Git有什么独特之处?Git的好处在于轻便。它只需要普通的执行程序,不需要安装服务器。如果你是单机小规模程序开发,装个Source Safe很简单实用。因为单机编程轻便最重要。单机上装个 CVS,Subversion,TFS 似乎太累赘。倒是Source Safe也是很轻便的。在众多反对Source Safe的浪潮中,有时候我会挺它,因为冲着它的轻便。个人简单使用的情况问题不大。但是如果功能需求复杂了Source Safe似乎缺点大于优点,如果讲成故事大概负面的多。相反Git既轻便,而且小到单机规模,大到全世界上百人同时做Linux核心开发。选用 Git 对于各种规模的开发团队应该都没有问题。
Git的提交还有个不同寻常的功能,它可以被施加到其它分支上。比如我有两个分支(branch),这两个分支暂时无法合并(merge),而第二个分支需要第一个分支里的一个提交所做的改动。这时候我可以用Git的Cherry Pick(采草莓),从第一个分支的一堆提交中采出一个来施加到第二个分支。
这样自然就牵涉到如何给提交写注释的问题。在老式的版本管理系统中,我们常常会为一个阶段做提交。比如一个提交被注释为“加入了jQuery”。它表示到提交的那个时刻你做到什么程度了。在老式的版本管理系统里也就只能做到记录下这一步。当时Git不同,在Git里这个提交有可能可以被用到其它分支,所以通常可以注释成“加入jQuery”。查看程序历史的步骤,你就会看到一个可以重复利用的步骤。如果你的程序历史是一步步具体的做法,而不仅仅是一本哪天做到什么程度的流水帐,它就有重复利用的价值了。
不过这样打造历史是举手之劳呢,还是需要花费额外时间精力以至于不大值得去做。这个问题我还在进一步摸索中。建议读者也加以思考。
最终无论你如何组织你的程序的历史,它在Git Extensions中都将一目了然。
6. 分支与合并
分支(branch)这个概念存在已久。它的主要作用是为开发中的程序创建相对独立的环境。通常程序的不同版本在它们各自的环境,也就是分支中进行,以避免冲突和相互覆盖。所以分支是版本管理系统的核心功能。
CVS,SourceSafe 等老一辈版本管理系统都可以做分支。甚至不用版本管理系统,只用文件系统你也可以做分支,为每个分支做个目录即可。
要注意的是分支不是关键。合并(merge)才是关键。不能很好地合并将极大妨碍创建分支的可行性。用文件系统、用CVS,SourceSafe等做的分支不能方便地合并。因此最好不要分支才算上策。分支长期以来几乎只是个梦想。
Git 给分支和合并带来了革命性的突破。它使得分支的成本很低,还及使得分支的合并很容易。你会感觉到使用分支的束缚全部没有了。天地间突然变得开阔,心情及其舒畅。前面的故事提到 Git 的轻便是我喜欢它的一个原因。毫无束缚的分支是我喜欢 Git 第二个原因。
Git 使得分支的成本很低是得益于它的分布式(Distributed)设计。其核心思想是把仓库建立在本地硬盘上。当需要和远地的(比如服务器上的)仓库协调的时候,Git 有命令来做同步。这在以后专门有一集来介绍。
Git 的分支的操作也在本地硬盘上进行,所以Git 的分支速度很快。Git 的分支也不需要文件目录。当你从一个分支转到另外一个分支的时候,Git 会自动重建现场,你的工作目录里的文件会恢复到你离开这个分支已经的状态。例如,在一个分支里先有3个文件,然后你开了一个新的分支,并提交了几个新文件以及修改了2个文件。当切换到第一个分支的时候,你的工作目录里恢复到原先的3地个文件,其内容也是先前的内容,没有改动过。所以Git 的分支非常方便。
Git 的分支的快速和方便不但打破了以前做分支的禁忌,而且促成了质变。它使得分支不仅仅用来管理程序的版本,而且可以用来辅助尝试性程序开发,称为主题分支/专题分支(Topic Branch)。
因此用熟了 Git 的人常常会在做任何事情以前开个分支先,如果成功就合并到主分支,如果不成功则简单删了这个试验分支。比如下图里我的 GitScc 仓库。它有4个分支,VS2008 是用于 Visual Studio 2008 的发布分支(production branch),master 是用于 Visual Studio 2010 的发布分支,dev-V0.7.0 是继续开发下一版的分支。Opt-Refresh是个分支用来尝试一个新的刷新算法。这个主题分支的存在丝毫不影响新功能的开发。如果新算法成功,我可以可并到 master 分支做个更新版本。如果不成功删掉这个分支即可。
有时候也会忘记先开分支,直到改了一批文件后才发现我应该先开个分支。这时候不用紧张,还来得及当场现开个分支。等提交修改的内容的时候,它们会在新开的分支里出现,而不是在老的分支里。再简单不过了。
即使你已经提交了修改内容也很容易修正过来。下面就是个例子。我后悔在 master 分支里把我的网站改 ASP.NET Pages 项目了。应该开个分支来放这个变化的。这时候该怎么办?
亡羊补牢,现开个分支。
回到 master 分支 (checkout master),然后设置 master 分支的位置回到从前,并使用 Hard 选项。
现在看下图,我满意了。改 ASP.NET Pages 项目的步骤放到了它自己的分支。master 分支恢复到先前状态。
能这么方便归功于 Git 在本地保存着完整的历史信息。今后我还会有专门一集故事来讲如何整理历史信息。而在下一集会我先分析 Git 拥有突破性分支与合并背后的的技术背景。
7. 三向合并
上一集讲到分支不是关键。合并才是关键。Git 在分支方面能得到突破性应用。其原因就是因为它利用了所谓的三向合并 (three-way merge) 技术。
合并要解决的主要矛盾是自动处理冲突 (conflicts)。如果要把下面两个分别来自两个分支的文件合并起来,合并程序会报 2 个冲突。它们都是程序无法自动处理的冲突。需要人工介入来为每处冲突做出选择。这是传统的二向合并 (two-way merge) 面临的困境。
三向合并则是在合并两个文件的同时参照第三个文件来判定文件内容变化的情况。合并的结果将采用变化过的新的内容。比如上面例子中的 Feature 5 和 Feature 6 是有冲突的两行内容。合并工具不知道应该采用哪一行。如果有了一个参照文件,合并工具就可以知道 Feature 5 是老的过时的,Feature 6 则是新的应该被采用的内容。合并工具就能自动解决冲突,不需要人工介入而完成文件的合并。
三向合并可以大大提高自动解决冲突的成功率,使得文件合并效率大增,人工介入的需求大幅减少。Git 在合并分支的时候,先追溯每个分支的历史。找到分支的共同祖先,并把这个祖先作为基础加入合并过程,进行成功率很高三向合并的。
目前三向合并已经成为众多版本控制系统的必备功能。不过与和它们比较,Git 的历史追溯速度是最快的。 Linus Torvalds 被称为速度怪物 (speed freak)。可见他追求速度到了极点。他开发的这个 Git 是一定要快的,因为他常常需要合并巨大的 Linux 核心源程序库。慢了可不行。
最终的结果就是用 Git,合并分支成不再是一个噩梦,而几乎是一种享受。
8. 整理历史
故事发生在我第一次发布 gitscc 到 github.com。
GitHub 是目前最好的以 Git 为核心的开源程序发布网站。开源程序发布网站最早的老大哥是 SourceForge (sf.net),它以 CVS 为核心。我曾经在 SourceForge 玩过一段时间,学习会了用 SSH。后来居上的是 Google Code,它是以 Subversion 和 Mercurial 为核心。Google Code 我没有玩过。我主要用了 CodePlex。在 CodePlex 上我有好几个项目。CodePlex 支持 TFS, Subversion 和 Mercurial。但是我一个也不喜欢,我只喜欢 CodePlex 清晰的界面和它的 Wiki。所以我的项目主页在 CodePlex,而源程序在 GitHub。
第一次发布到 github 的时候,其实对 Git 的功能还不甚了解,还以为是象 TFS 那样把当前的版本发布出去。结果吓一跳,它把我的每一个步骤也就是每一个提交 (commit) 都发表了。被吓到的原因倒不是 Git 同步了本地和服务器的仓库。而是我程序的历史乱糟糟的。以前用 SourceSafe 或者 TFS 的老习惯是根本不在意程序的历史,甚至可能连提交的注册也懒得写。一下全部被公布出来实在没有面子。于是我立即删掉了服务器上的仓库,回来讲究如何整理好历史以后再发布。
有一点要特别注意,整理历史只能是修改还没有发布出去的提交。否则造成天下大乱要挨骂的。以前讲三向合并的时候提到 Git 要往前追溯历史。如果把公开发布的历史改了,其他人就找不到源头天下大乱了。
下面要讲两种整理。整理提交和整理分支。
先是整理提交的例子。这就是用 git rebase -i 来把下图中零零碎碎的一批提交合并起来。
目前Git Extensions 和 TortoiseGit 都没有很好的 rebase -i 界面。好在 rebase -i 本身还算直观。下面我把三个提交做了合并 (squash)。
整理提交后,下图中 master 分支干净多了。
接下来是整理分支的例子。继续上图,razor 分支 是个主题分支 (topic branch),用来做实验的。假设实验成功,我们应该分支合并 master 与 razor 两个分支。然后删除 razor 分支。
问题来了,程序历史中留下了一个奇怪的圈。它本身没有错,是很准确的历史记录。但是看起来很不舒服。我比较喜欢用 rebase 整理来主题分支。
这样 master 分支和 razor 分支都在一条线上了。这个时候合并 master 与 razor 两个分支,在中 Git 是个快进 (fast forward)。然后 razor 分支最终完成使命,可以被删除了。
经过整理提交 (rebase -i) 和整理分支 (rebase),我的程序历史被整修得很干净很挺刮。发布出去也不难为情了。
最后重复一下要点,整理工作一定要在发布以前做。
9. 中央仓库
传统的版本管理系统从 CVS, SourceSafe,Subversion 到 TFS 被称为集中式版本管理系统 (centralized version control)。它们只有一个仓库,一个中央仓库。开发人员从一个中央仓库上传下载源程序,他们的程序都被有效地管理起来。
Git,Mercurial, Bazaar 等被称为分布式版本管理系统 (distributed version control)。它们不止只有一个仓库,它们有很多个仓库。开发人员人手一个甚至数个仓库。这些分散在各处的仓库可以相互沟通协调起来。比如,在 Git 里可以加入远程仓库 (remote) (比如服务器上的)仓库。然后用 push 和 pull 命令来完成和远程的仓库协调。
注意,我说的是分布式版本管理系统不止只有一个仓库,而不是说分布式版本管理系统不能有一个中央仓库。千万不能误解误传分布式版本管理系统不能有中央仓库。相反团队开发必须要有一个中央仓库作为官方的源程序和历史的基地。Pro Git 中称它为 blessed repository,神圣的仓库。象 github.com, repo.or.cz 这样网站就是用来存放和发布各种项目的中央仓库。
同步协调 Git 本地仓库和远程仓库有很多种协议可用,例如 SSH 之类的。而且从一开始 Git 就支持 HTTP/HTTPS 协议的。HTTP/HTTPS 很重要,它很轻巧,能通过各种防火墙。问题是 Git 一开始并没有注重 HTTP/HTTPS,只提供了有限的通过HTTP/HTTPS下载 (pull) 和通过 WebDavs 上传 (push)。2008 年 Google 决定为 Google Code 选一个分布式版本管理系统的时候,他们所做的一份著名的调查报告就因为 HTTP/HTTPS 支持这一个致命伤判定了 Git 输给了 Mercurial。所以现在 Google Code 用的是 Mercurial 而不是 Git。
打那以后 Git 从 1.6.6 版以后 (现在 Git 已经远远超过 1.7) 增加了对 HTTP/HTTPS 的支持,使得它 HTTP/HTTPS 协议与 Git 内置,SSH 等协议一样高效。Github 不失时机立即也推出了 HTTP/HTTPS 支持。现在我上 github 全部而且只用 HTTPS。
你的开发团队如果想自己建立一个支持 HTTP 的 Git 中央仓库,尤其是要运行在 Windows 和 IIS 下面该怎么做呢?
在找不到现成的系统的时候,我毅然决定再做一个开源项目 gitweb。它完全彻底地用了 Windows 下的办法,比如 ASP.NET HttpHandler, IIS 7 integrated pipeline mode。小巧高效。
gitweb 的主页是 http://gitweb.codeplex.com。 欢迎使用和参与开发。
Git 的中央仓库与集中式版本管理系统 (CVS, SourceSafe,Subversion,TFS 等)的中央仓库又有什么区别?
Git 的中央仓库可以整个复制下来 (clone) 到你的本地硬盘。你的本地有全部的程序历史。这样即使无法连接在服务器上,你也可以查看整个的程序的历史记录,这点是 TFS 等集中式版本管理系统做不到的。用 TFS 等你可以离线编程,但是看不到历史,看不到分支。
在 Git 的中央仓库里,你可以创建和拥有你自己分支,在自己分支里做的事情不会影响到主分支和其他人的分支。这是一种沙盒模式(sandbox)。你可以任意提交 (commit) 你的修改,Git 不会麻烦你做合并(merge)。合并只有在分支合并的时候才发生。而在集中式版本管理系统,由于它们做分支比较复杂,不大可能每个人拥有多个分支。常常是多人一个合用分支,这样你每次提交都面临需要合并的可能。当然你可以说这可以通过有效组织管理来解决,比如项目有计划地分配给开发者以避免冲突。所谓的计划、管理和规则,讲到底都是限制。限制了使用者应用灵活性的同时,也显示了系统只能提供有限的功能。
在 Git 里很多限制没有了。你拥有了更多灵活性。但是你也可能感到无从入手了,以往集中式版本管理系统的经验不再适用。因此下一集将介绍协同开发的几个重点。
10. 协同开发
上一集讲到了中央仓库。也许中央仓库的提法会被误解为等同于集中式版本管理系统里的唯一的中央仓库。其实它们是有很大不同的。
在集中式版本控制系统里没有本地仓库,只有唯一的中央仓库。提交是直接交到中央仓库。集中式的中央仓库是唯一的程序和历史的保存地。一旦它损坏,整个系统就垮台了,源程序和历史都无从找起。因为本地没有完整的仓库内容。
而在分布式版本控制系统里,仓库是在本地,提交是提交到本地仓库。本地仓库和服务器上的仓库保持同步,团队每个人有一份完整的仓库。本地仓库、服务器仓库哪个坏了都没有问题。可以从任何一个没有损坏的仓库中复制一个回来找回你的程序。从这个意义上讲发布式的仓库个个平等,没有中央和地方的差别。
但是这似乎造成了鸡生蛋,蛋生鸡哪个先的问题。所以我们需要定义一个放在公共服务器上的仓库,如 github上的仓库作为大家复制的源头。这也就是逻辑上的中央仓库。弄清了这些概念后,我们可以开始看如何协同开发。参与协同开发的人可以分成两种类型。项目主持人和项目参与人。
主持项目人就是创建仓库,控制其主分支 master。并把这个仓库发布到服务器成为中央仓库。项目参与人复制中央仓库,建立主题分支进行开发。他复制中央仓库可以用 clone 直接复制到本地,也可以用 fork 先复制在服务器上(如果服务器支持的话),然后再 clone 复制到本地。
用 fork的好处是参与者完成开发把程序上传到 fork 以后,可以请项目主持人直接到 fork 里去取。这个请求称为 pull request。如果没有 fork,项目参与人也可以把开发的内容打包成补丁(format patch)再 email 给主持项目人。项目主持人把所有参与者的修改从 fork 里或者从补丁里合并到 master 分支。
下面是个例子。我是 gitweb 的项目主持人。我控制着在 github 上的中央仓库。它已经被 4 个人 fork 过去。其中有个人给修改了一些功能,并给我发来了 pull request。
于是我就把他的修改下载下来,合并进 mater 分支。
然后,再上传合并后的 master。
Github 检测到我上传了 pull request 里的修改内容, 自动关闭了 pull request。还相当聪明。
11. 图形显示
在读 Pro Git Book 的时候觉得它用图来解释程序的提交过程和分支合并非常直观。
Git Extensions 等 Git 图形界面都能用图的形式来表达程序的演变历史。
我一直很好奇,想了解这是怎么做的。尤其是想看看是否能在网页里做类似的显示。于是就研究了一下,最后找到了答案。我设计了个简单有效的数据结构。用一个节点列表和一个连接列表来描述 Git 程序历史的图形。再用 WCF Data Service 以 OData 格式发布。在客户段网页里可以用 jQuery 来读取这个数据,然后画到 HTML5 的 canvas 里。
效果不错。接下来我还准备试试 SVG。
源程序已经发布在我的开源项目 Git Web Access。