首页 > gforge > subversion VS cvs

subversion VS cvs

 

 

CVS

CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。工作模式如下:

CVS服务器(文件版本库)

CVS(Concurrent Version System)版本控制系统是一种GNU软件包,主要用于在多人开发环境下的源码的维护。实际上CVS可以维护任意文档的开发和使用,例如共享文件的编辑修改,而不仅仅局限于程序设计。CVS维护的文件类型可以是文本类型也可以是二进制类型。CVS用Copy-Modify-Merge(拷贝、修改、合并)变化表支持对文件的同时访问和修改。它明确地将源文件的存储和用户的工作空间独立开来,并使其并行操作。CVS基于客户端/服务器的行为使其可容纳多个用户,构成网络也很方便。这一特性使得CVS成为位于不同地点的人同时处理数据文件(特别是程序的源代码)时的首选。

所有重要的免费软件项目都使用CVS作为其程序员之间的中心点,以便能够综合各程序员的改进和更改。这些项目包括GNOME、KDE、THE GIMP和Wine等。

CVS的基本工作思路是这样的:在一台服务器上建立一个源代码库,库里可以存放许多不同项目的源程序。由源代码库管理员统一管理这些源程序。每个用户在使用源代码库之前,首先要把源代码库里的项目文件下载到本地,然后用户可以在本地任意修改,最后用CVS命令进行提交,由CVS源代码库统一管理修改。这样,就好像只有一个人在修改文件一样,既避免了冲突,又可以做到跟踪文件变化等。

CVS是并发版本系统(Concurrent Versions System)的意思,主流的开放源码网络透明的版本控制系统。CVS对于从个人开发者到大型,分布团队都是有用的:

   它的客户机/服务器存取方法使得开发者可以从任何因特网的接入点存取最 新的代码。它的无限制的版本管理检出(check out:注1)的模式避免了通常的因为排它 检出模式而引起的人工冲突。 它的客户端工具可以在绝大多数的平台上使用。

   CVS被应用于流行的开放源码工程中,象Mozilla,GIMP,XEmacs,KDE,和GNOME等。 那么它到底怎么样?

你可能会说,它非常棒,但是对于”我”来说它能做什么?首先,基本的 :一个版本控制系统保持了对一系列文件所作改变的历史记录。对于一个开发者来说,那就意味着在你对一个程序所进行开发的整个期间,能够跟踪对其所作的所有改动的痕迹。对你来说,有没有出现过由于在令行上 按错键而导致一天的工作都白费的情况呢?版本控制系统给了你一个安全的网络。

版本控制系统对任何人都有用,真的。(毕竟,谁不愿意使用一个安全的 网络呢?)但是它们经常被软件开发团队使用。在团队中工作的开发者需要能够调整他们的各自的修改;一个集 中式版本控制系统允许那样做。

代码集中的配置

个人开发者希望一个版本控制系统的安全网络能够运行在他们的本地的 一台机器上。然而,开发团队需要一个集中的服务器,所有的成员可以将服务器作为仓库来访问他们的代码。在 一个办公室中,没有问题 –只是将仓库连到本地网络上的一台服务器上就行了。对于开放源码项目…噢, 还是没有问题,这要感谢因特网。CVS内建了客户机/服务器存取方法,所以任何一个可以连到因特网上的开发 者都可以存取在一台CVS服务器上的文件。

调整代码

在传统的版本控制系统中,一个开发者检出一个文件,修改它,然后将 其登记回去。检出文件的开发者拥有对这个文件修改的排它权。没有其它的开发者可以检出这个文件 — 并且只 有检出那个文件的开发者可以登记(check in:注2)所做的修改。(当然对于管理员有很多方法可以超越这个 限制。)

想一下排它的检出可能会如何工作:Bob的兄弟检出 foo.java以便加入 注释,写好代码后他什么也没做。然后他去吃午饭了。Bob吃完午饭后,发现他的老板所指给他的一个bug在 foo.java里。他试图检出 foo.java … 但是版本控制系统不允许他这样做,因为他的兄弟已经把它检出了。Bob不 得不等着他的兄弟吃完午饭回来(在这个”好”日子用了两个小时),他才可以修正bug。

在一个大型的开放源码工程中,因为开发者可能在任意的时区工作得很 晚,给予一个开发者阻止任意地方的其它开发者继续处理任意文件的能力很明显示无法运转。他们最终将因为不 能够在他们想要的时候开展项目而感到厌烦。

CVS通过它的无限制的检出模式解决了这个问题。检出一个文件并不给定 开发者对那个文件的排它权。其它的开发者也可以对其检出,进行他们自己的修改,并且将其登记回去。

  ”等一下!”你可能会说。”但是后面的登记不是会覆盖前面的吗?”回答 是不会。详细地回答就是当多个开发者对同一个文件作了修改CVS会检测,并且自动合并那些改变。

到现在为止,你已经毫不犹豫地着迷于CVS 的潜力,并且急不可待地想 开始。第一步就是去得到 适合你的平台的CVS软件。安装CVS通常就是将其从你下载的压缩包中解开这么一件 事。配置CVS 可能要小心一些,它非常依赖于你使用的平台和你的CVS代码仓库的存放地。CVShome.org存放了大 量的CVS 文档:

《Introduction to CVS》 Jim Blandy所写的一篇很棒地在线介绍。我也推荐《 Open Source Development with CVS》 Karl Fogel写的。你可以读一下我写的关 于它的评论在OpenAvenue VOX上。Karl已 经将书中关于CVS的部分置于GPL许可证之下;这篇文档在Karl的站点上以多种文档格式提供。 《The Cederqvist》 — 由Per Cederqvist所编写的CVS手册 — 是一个关于CVS信息的全面资料。  有大量的可用在许多平台上CVS 附加工具,它们给 CVS增加了功能或使得CVS更容易使用。

 

 

subversion

subversion:新一代的开源版本控制工具,CVS的下一代。具有以下特性:

# 具有CVS大多数的当前特性

Subversion意味着更好的CVS,所以它具有大多数CVS的特性。大致说来,Subversion的接口在某些特性上与CVS很类似,若没有特殊原因让你选择其他的解决方式,就采用Subverioin。

# 目录、重命名以及文件元数据都被标示了版本

不具有这些特性是大多数CVS用户所抱怨的。Subversion的版本不仅仅是文件内容以及文件是否存在,还包括目录、副本和重命名。同时它还允许任意的元数据(属性)与任何文件或目录一起标示版本,并且提供了一种用于在文件上标示‘执行’权限标志的版本机制。

# 提交具有真正的原子性

在整个提交没有成功之前任何提交的部分都不会生效。修订(Revision)数量是根据每次提交而定的,并不是每个文件;日志信息附加到了修订内容中,不会像CVS那样冗余。

# 可以选择Apache服务器,使用WebDAV/DeltaV协议

Subversion 可以使用基于HTTP协议的WebDAV/DeltaV协议作为网络通讯协议,并且Apache Web服务器提供了仓库端的网络服务。这就使得Subversion在互操作上比CVS有优势,并且提供了很多免费的关键特性:认证、基于路径的认证、压缩、基本的仓库浏览。

# 可以选择独立运行

Subversion同时也提供了一个以独立服务器运行的选项,它使用自定义的协议(并不是每个人都希望运行在Apache 2.x之上)。独立的服务器可以运行为inetd服务,或者deamon模式,并且提供基本的认证和授权。它可以经由ssh访问。

# 分支(Branching)和标签(Tagging)的开销更小(具有固定的时间)

没有任何理由让这些操作更费时,所以它们必须如此。分支和标签都使用底层的“复制”操作来实现。复制操作使用很小的,固定大小的空间。任何复制都是一个标签;如果你开始提交一个副本,那么它也就会成为一个分支。

# 天然的C/S结构、分层的库设计

Subversion从一开始就被设计成C/S结构的;这样就避免了一些折磨着CVS的维护问题。代码被组织成具有明确的接口的模块,设计成被其他程序调用。

# C/S 协议在两个方向上发送diff

网络协议在两个方向传输diff时能够高效的使用带宽。(CVS发送diff从服务器到客户端,而不是从客户端到服务器)

# 开销与更改的大小有关而不是数据大小

一般而言,Subversion操作所需的时间和操作所导致的结果的大小是成比例的,并不是项目更改发生时的绝对文件大小。这是Subversion仓库模型的一个特性。

# 可选择数据库或者普通文件作为仓库实现

仓库可以使用一个内嵌的数据库支持(BerkeleyDB)或者使用普通的平面文件,两者都使用自定义的格式。

# 给符号连接标示版本

Unix用户可以将符号连接加入版本控制。这些连接可以在Unix中被再次创建,在Win32中则不可以。

# 处理二进制文件更高效

Subversion在处理二进制上和处理文本文件一样高效,因为它使用二进制比较算法传输和存储成功的修订版本。

# 可解析的输出

所有Subversion命令行客户端的输出都是仔细设计成方便人们阅读并且自动解析;脚本具有更高的优先级。

# 本地化消息

Subversion使用gettext()来显示翻译过的错误、报告和帮助信息,基于当前的区域设置。

 

Subversion与CVS的对比——功能性对比

一、Subversion包含绝大部分CVS功能

Subversion 作为CVS 的重写版和改进版,其目标就是作为一个更好的版本控制软件,取代目前流行的CVS。Subversion 的主要开发人员都是业界知名的CVS 专家。Subversion支持绝大部分的CVS 功能/命令;Subversion 的命令风格和界面也与CVS 非常接近。当然,不同的地方正是对CVS 的改进。

二、全局性的版本编号

一个新的版本,并得到一个自增量的版本号N+1,该版本号并不针对某个特定的文件,而是全局性的、针对整个版本库的。因此,我们可以将Subversion 的版本库看作是一个文件系统或文件目录树的数组。

从技术的角度来说,在Subversion 中,“文件foo.c 的第5 版本”这个说法是错误的;正确的说法应该是:”文件foo.c 在版本库被修改了5 次,即执行5 次commit 后是什么样子?”。显然,在Subversion 中,版本库被修改5 次后foo.c 的内容,和被修改了6 次后foo.c 的内容很可能完全一样,因为版本库的第6 次修改很可能只修改了版本库的其他部分,而并没有对foo.c 的进行修改。相反,在CVS 中,文件foo.c 的第1.1 版本和第1.2 版本总是不同的。Subversion 的全局性版本编号为Subversion 带来了诸多的优势:如对目录或文件执行拷贝,无论涉及多少文件,Subversion 不需要对单个文件依次执行拷贝命令,仅仅需要建立一个指向相应的全局版本号的一个指针即可。

三、目录的版本控制

CVS 只能对文件进行版本控制,不能对目录进行版本控制,因此CVS 没有任何关于文件“移动”(move) 操作的概念。当人为进行文件移动操作时,CVS 只能注意到,一个文件在一个位置被删除了,而在一个新位置创建了另外一个文件。由于它不会连接两个操作,因此也很容易使文件历史轨迹丢失。设置 CVS 存储库时,必须非常谨慎地为每个文件选择准确的位置,因为在设置之后,几乎就要一直使用这个位置了。

同样由于CVS 不记录目录的版本历史,CVS 不支持对文件的“重命名”(rename),人为的对文件进行重命名会使得命名前后的文件失去历史联系,而记录历史本来是版本管理的主要目的。还有,CVS 不支持对文件的“拷贝”(copy),人为的拷贝对CVS 而言,只能看到新的文件的增加,而不能记录拷贝源文件和目标文件之间的联系。

综上所述,缺乏对文件“移动”、“重命名”、“拷贝”的支持的根源在于CVS 不能记录目录的版本历史,而这些操作在当前的软件开发过程中经常发生,这正是Subversion被开发并取代CVS 的主要原因之一。

Subversion 将目录作为一类特殊的文件来处理(事实上,从文件系统的角度来看,目录确实是一类特殊的文件,当目录中的子目录/文件被删除、重命名、或新的子目录/文件被创建时,目录的内容将发生改变)。因此,Subversion 象记录普通文件的修改历史一样记录对目录的修改历史,当发生文件/目录的移动、重命名或拷贝操作时,Subversion 能够准确记录操作前后的历史联系。同样,象对文件的不同历史版本进行比较一样,Subversion支持对目录的不同历史版本的比较,清晰展现目录的变化历史。

四、原子性提交

从使用者的角度来看,CVS 和Subversion 都支持对多个文件修改的批量提交,但二者在实现方式上存在本质的区别。

CVS 采用线性、串行的批量提交,即依次地,一个接一个地执行提交,每成功提交一个文件,该文件的一个新的版本即被记录到版本库中,提交时用户提供的日志信息被重复地存储到每一个被修改的文件的版本历史中。

CVS 串行批量提交模式的弊端在于 - 当任何原因造成批量操作的中断时(典型原因包括:网络中断、客户端死机等),版本库往往处于一个不一致的状态:原本应该全部入库的文件只有一部分入库,很有可能版本库中的最新版本不能顺利编译,更为严重的是,随着其他的用户执行cvs update 操作,该不一致性将迅速在开发团队中扩散,从而严重影响团队的开发效率,并存在质量隐患。另外,假如该批量提交的中断没有被及时发现,开发团队往往要花更多的时间进行软件调试和排错。

CVS 即使在批量提交不发生中断时也会造成不一致:假设用户A 启动一个需要较长时间才能完成的批量提交;与此同时,用户B 执行cvs update 操作。此时,用户B 很有可能得到一个不一致的更新,即用户B 通过“更新”操作,得到用户A 的部分修改文件。

Subversion 彻底消除了CVS 的以上弊端。无论批量提交包含多少文件修改,只有当全部文件修改都成功入库,该提交才变得有效,才对其他用户可见;否则,无论任何原因造成中断,Subversion 都会自动执行“回滚”(rollback)操作。换一个说法,Subversion 保证所有的修改要么全部入库生效,要么一个也不入库,即对版本库不作任何的修改。这就是Subversion 的原子性提交(atomic commit)。

由于Subversion 的原子性提交特性和全局版本编号方式,当提交成功完成时,一个唯一的、新的全局版本编号产生,而提交时用户提供的日志信息与该新的版本编号关联,只进行一次存储(区别于CVS 的按文件重复存储)。

五、支持变更集概念

由于Subversion 的所有提交是原子性的,每次成功提交形成的唯一的全局版本号对应此次批量提交的所有文件修改,也就是说,一个Subversion 版本号其实对应了一个逻辑上的变更集(change set),该变更集可能对应于对一个BUG 的修复,或者对应于对一个已有功能的改进,或者对应于一个新功能的实现。可以说,变更集是一个软件开发活动的逻辑结果,该变更集可以通过其对应的版本号在软件开发的其他过程中(如软件合并/集成过程,软件发布管理,变更管理系统,缺陷追踪系统)被引用。因此,Subversion 将版本管理从单纯的、单个的文件修改的层次通过逻辑上的抽象,上升到更便于理解和交流的开发活动的层次。

六、差异化的二进制文件处理

由于历史原因,CVS 主要是为早期的程序员设计的,CVS 能够有效处理文本文件(或ASCII文件,源代码文件),可以对文本文件进行差异化的存储、新旧版本的比较,文件合并等;但对于二进制文件,CVS 则明显力不从心。在CVS 的版本库中,对于二进制文件的历史版本,CVS 唯一能做的就是对不同的版本进行独立的、冗余的存储,哪怕版本之间其实只存在微小的差异。举例而言,一个10M 的二进制文件(照片、图形文件、机械设计文件、电子设计文件)假如每周修改一次,无论每次修改的大小,一年下来,仅该文件就要消耗500M 以上的存储空间。而且,客户端每次获取该文件的新版本都要消耗10M 的网络流量。

对于目前的开发团队,无论是软件开发,Web 站点的开发,手机等电子产品的研发,需要进行版本管理的不仅是源代码等文本文件,还需要管理需求文档、设计文档、测试文档、用户手册,图形图像文件,机械/电子设计文件等诸多的二进制文件,CVS 显然不是一个好的选择。

与CVS 不同,Subversion 采用统一的二进制差异算法(binary differencing algorithm),即对文本文件和二进制文件采用相同的差异比较算法,并以相同的方式在版本库中进行存储:每次提交后版本库中只存储相对于先前版本的差异,从而可以节省大量的存储空间。该二进制差异算法不仅应用在版本的存储上,更为重要的是,Subversion 对二进制文件与文本文件一视同仁,当客户端需要获取新的版本时(如执行svn update),在网络上只有版本的差异被传输,从而大大减少对网络带宽的消耗。更多细节参见“七、双向的差异化-压缩网络传输”。

七、 双向的差异化-压缩网络传输

如上所述,CVS 对二进制文件不能进行有效的差异化处理。对于文本文件,CVS 仅仅支持单向的差异化传输:从CVS 服务器到客户端的传输是差异化的,即执行cvs update 时,只有差异的部分从服务器传输到客户端;而当执行cvs commit 时,无论代码变化多少,CVS 都需要从客户端向服务器完整传输被修改文件的全部内容,不能只传输差异。

相反,无论是文本文件还是二进制文件,Subversion 都进行双向的差异化传输,并且差异化内容还要进行压缩/解压缩的过程:在服务器端获取差异显而易见,与CVS 类似;Subversion 在客户端获取差异的秘密在于 — Subversion 在客户端的工作拷贝中隐含了每个文件的一个“只读的、干净的”副本(该副本隐藏在隐含目录.svn 里,通常不可见,该副本还有更多的妙用,参见“十二、更多的本地/离线操作”),通过比较用户在客户端的修改和该隐含的副本,Subversion 获取需要真正传送到服务器的差异,并对差异进行压缩后才进行网络传输。

对CVS 而言,操作的成本(网络带宽消耗是最大的操作成本)与被修改的文件的大小成比例,而与修改本身的大小无关;对Subversion 而言,操作成本只与修改本身的大小成比例,而与被修改的文件的大小无关。因此,与CVS 相比,Subversion 消耗更少的网络带宽(以客户端的存储空间换取更少的带宽消耗在目前的计算环境下应该是个相当不错的选择!)。Subversion 更加适合基于互联网(或广域网)进行协作开发的地理上分布的团队 — 版本服务器集中、单一;客户端广泛分布。

八、高效、快捷创建分支和基线

CVS 和Subversion 都支持分支(branch)和基线(tag),通过分支与合并,可以有效支持大项目的并行开发模式;通过基线管理,可以准确标识一组文件的版本,有效进行软件发布管理和必要时的历史回溯。

但CVS 和Subversion 在实现分支和基线的方式上存在很大的不同。CVS 在创建分支的时候,需要对所有进行分支的文件进行依次的操作,因此分支的建立成本(主要是建立分支所需的时间,或消耗的计算资源)与参与分支的文件数量成比例,项目越大,版本库越大,文件越多,分支的建立成本越高;基线(tag)的建立与此类似。

Subversion 的分支和基线是通过执行“拷贝”来建立的:回想一下在没有引入版本管理工具的时候我们是如何进行所谓的“分支”和“基线”管理的?答案显然是“拷贝” — 我们通过“拷贝”或“备份”来建立基线;同样,为支持多个开发人员可以同时进行开发,我们为每个开发人员创建一份“拷贝”。由此看来,Subversion 通过“拷贝”来建立分支和基线显得非常自然,有点“返朴归真”的意思。

由于Subversion 的全局版本号特性,Subversion 中分支或基线的创建过程,或Subversion中的“拷贝”过程,真正的操作是在版本库中创建一个到某一全局版本号的指针(pointer),不再需要针对众多的单个文件依次执行操作。因此,该操作的成本为一个很小的常数,与项目大小,版本库大小,文件数目的多少无关;并且,分支或基线的建立不需要进行版本的冗余存储,新建立的分支或基线基本不占用版本库空间,分支的后续存储空间的开销也只与修改的大小有关。

九、集成Apache Web Server,提供更多的特性

Subversion 通过与Apache Web Server 的集成,可以提供基于http/https 协议的版本库访问机制,从而支持Subversion 跨越防火墙的安全访问。除此以外,Subversion 还可以利用更多的Apache 特性,包括但不限于:Apache 丰富的用户认证机制(包括通过LDAP服务器如Windows Active Directory 服务器的用户认证),基于目录路径的精细粒度的访问控制,对传输的网络流量进行压缩/解压缩,浏览版本库目录结构等等。

Elliotte Rusty Harold 介绍了 Subversion —— 一种开放源码、多用户版本控制系统,支持非 ASCII 文本和二进制数据。通过 Elliotte 的介绍,您可看到如何在 Eclipse 中配置 Subversion 支持(通过 Subclipse 插件)、检出一个项目、与您的存储库同步,随后执行一些常用的操作,如合并、修补、比较和删除。

版本控制

版本控制之于程序员,就好比安全网之于高空秋千表演者。知道安全网就在那里,万一自己摔落它能够提供保护,高空秋千表演者才能放心大胆地在空中飞跃。同样,版本控制使您有能力去冒以往想都不敢想的风险。如果哪儿出了错,您总是可以使自己的代码回复到一个已知的、工作正常的版本。您可以在不触及主干的分支中进行试验,而不会影响到其他小组成员。在已经发布的产品的较老版本中发现 bug 时,您可以轻松检出特定版本,以确认、修订,并生成该 bug 的修补程序。如果没有版本控制,您必须极为慎之又慎,缓慢地推进,总而言之,生产力会更低。

Subversion 是一种开放源码的全新版本控制系统,支持可在本地访问或通过网络访问的数据库和文件系统存储库。不但提供了常见的比较、修补、标记、提交、回复和分支功能性,Subversion 还增加了追踪移动和删除的能力。此外,它支持非 ASCII 文本和二进制数据,所有这一切都使 Subversion 不仅对传统的编程任务非常有用,同时也适于 Web 开发、图书创作和其他在传统方式下未采纳版本控制功能的领域。

本文介绍了使用 Subversion 追踪项目的基础知识,以使您在编写代码时能够承受更多的风险、享受更多的乐趣。

参考资料 部分)。

在开放源码程序员间,CVS 已成为一种事实上的 标准。Codehaus、Sourceforge、Savannah 和 Java™ 社区的 java.net 等站点中驻留的免费 CVS 使得为开源项目建设存储库更为简单。以 CVS 为中心,已发展起一个大型的附件市场,包括 TortoiseCVS、ViewCVS 和 Fisheye 等工具。

与其他版本控制系统相比,CVS 最令人称道的地方就是其非锁定 存储库,这使多个开发人员能够同时检出同一个文件。CVS 在提交时解决冲突问题,这就避免了冲突成为发展的瓶颈。CVS 第二个出色的特性就是它是一种网络存储库。处于许多不同系统上的程序员可以通过公共的 Internet 访问相同的存储库。

逐渐失去优势的 CVS

CVS 已经不再适合现代开发,这一点越来越明显。特别是 CVS 只能满足老式 C 程序员的 ASCII 需求,而对 Web 开发人员和其他非传统用户来说,CVS 实际上根本不起作用。在您开始考虑存储整个 Web 站点时,在 CVS 中,将文件从一个目录移动到另外一个目录是关键考虑事项。因此,在几年前,许多核心 CVS 开发人员认为,已经到了利用他们多年来使用 CVS 时学到的经验和教训、从头开始创建新一代开放源码存储库的时机。在 2004 年年初,他们的努力结出了丰硕的果实,那就是 Subversion 1.0。

Subversion 的支持与采纳

程序员(特别是那些依赖版本控制的程序员)是一个非常谨慎的群体,Subversion 着实用了很长一段时间,才得到他们的广泛接受。很少有程序员愿意冲在易于流血的最前沿,即便是他们已经因为 CVS 受了伤。甚至是在 Subersion 变得可靠之后,仍然用了好几年的时间,所有第三方编辑器、IDE 和文档规范才相继跟进。而 Subversion 依然在不断改进,BBEdit 和 Eclipse 等第三方工具现在已经有了足够好的 Subversion 支持。逐渐地,新项目也纷纷选择 Subversion 满足其版本控制需求,而老项目正在向 Subversion 移植。最近,Apache Software Foundation 已移植到 Subversion。已实现移植的项目包括 Xerces XML 解析器、Apache HTTP Server 和 Spamassassin。

通过 IDE 使用 Subversion,请见:http://www-128.ibm.com/developerworks/cn/java/j-subversion/index.html

结束语

对于内部存储库,Subversion 提供了远超过 CVS 的改进。如果添加了某种类型的彻底消除功能,它也应同样适于外部存储库。尽管像 Eclipse 这样的工具对 Subversion 的第三方支持还不像 CVS 支持那样普遍,但形势正在迅速地发生变化。Subversion 会成为新项目的默认源代码存储库。尚无源代码控制的现有项目应尽快检入 Subversion。而已有 CVS 存储库的现有项目可能仍在观望,希望等到他们所依赖的全部工具均全面支持 Subversion 后再进行切换。但这些项目最终也会移植到 Subversion。Subversion 中的改进如此显著,令人难以忽略。未来必将属于 Subversion。

来源: http://fayfaykong.blog.163.com/blog/static/171453442007114112527881/

 

 

关于作者:

昵称:Jack.shang
档案信息:jack.shang 一位从技术走向管理,再从管理走向市场的普通行者
联系方式:你可以通过syfvb@hotmail.com联系作者
点击查看发表过的所有文章...
本文永久链接: http://blog.retailsolution.cn/archives/2046

 

 

对本文的评价:

 

 

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
您必须在 登录 后才能发布评论.