內容目录

上一个主题

9.1. A. Git命令索引

下一个主题

9.3. Git和SVN面对面

本页

9.2. Git与CVS面对面

9.2.1. 面对面访谈录

Git:我的提交是原子提交。每次提交都对应于一个目录树(树对象)。因为我的提交ID是对目录树及相关的提交信息建立的一个SHA1哈希值,所以可以保证数据的完整性。

CVS:我承认这是我的软肋,一次错误或冲突的提交会导致部分数据被提交,而部分数据没有提交,版本库完整性被破坏,所以人们才设计出来Subversion(SVN)来取代我。

Git:我的分支和里程碑管理非常快捷。因为我的分支和里程碑就是一个记录提交ID的引用,你的呢?

CVS:你怎么又提到别人的痛处了!我的分支和里程碑创建速度还是很快的,...嗯...,如果在版本库中只有几个文件的话。当然如果版本库的文件的很多,创建分支、里程碑创建就需要花费更多的时间。有些人对此忍无可忍,于是设计出SVN来取代我。

Git:其实我不用里程碑都没有关系,因为每一个提交ID就对应于唯一的一个提交状态。

CVS:这也是我做不到的。我没有全局版本号的概念,每一个文件都通过单独的版本号记录其变更历史,所以人们在使用我的时候必须经常地用里程碑(tag)对我的状态进行标识。还需要提醒一句,如果版本库中文件太多,创建里程碑是很耗时的,因为要一一打开每一个版本库中的文件,在其中记录里程碑和文件版本的关系。

Git:我的工作区很干净。只在工作区的根目录下有一个:file:`.git`目录,此外再无其他辅助目录或文件。

CVS:我要在工作区的每一个目录下都放置一个CVS目录,这个目录下有个Entries文件很重要,记录了对应工作区文件的检出版本以及时间戳等信息。这样做的好处是可以将工作区移动到任何其他磁盘和目录,依然可以使用,甚至我可以将工作区的一个子目录拿出来,作为独立的工作区。

Git:我也可以将工作区移动到其他磁盘,但是要保证工作区下的:file:`.git`目录和工作区一同移动。也不可以只移动工作区下的一个目录到其他磁盘或目录,那样的话移出的目录就不能工作了。

Git:我的网络传输效率很高。在和其他版本库交互时,对方会告诉我他有什么,我也知道我有什么,因为只对缺失对象的打包传输,所以效率很高而且能够显示传输进度。

CVS:这一点我不行。因为我本地没有文件做对照,所以我在传输的时候不可能做到增量传输。

Git:我甚至可以不需要网络,因为我在本地拥有完整的版本库,几乎所有操作都是在本地完成。

CVS:我的操作处处需要网络,如果版本库是在网络中其他服务器上的话。如果网速比较慢,查看日志、查看历史版本都需要花费很长时间等待。

CVS:你怎么没有更新(update)命令?还有你为什么老是要执行检出命令(checkout)?对我而言,检出命令只在工作区创建时一次完成的。

Git:你的检出命令(checkout)是从远程版本库服务器获取数据完成本地工作区的创建,版本库仍然位于远程的服务器上。你的更新(update)命令执行的很慢对么?之所以你需要执行更新命令是因为你的版本库在远程啊。别忘了我的版本库是在本地,我的每一步操作工作区和版本库都是同步的,所以更新操作就没有存在的必要了。而我的检出(checkout)操作是将本地版本库的数据检出到本地工作区,用于恢复本地丢失的文件或错误改动的文件,也用于切换不同的分支。我也有一个和你的更新(update)操作类似的比较耗时的网络操作命令叫做:command:`git fetch`或:command:`git pull`,这两个操作是从别人的版本库获取他人改动。一般使用我(Git)做团队协作的时候,会部署一个集中共享的版本库,我就用这两个命令(:command:`git fetch`或:command:`git pull`)从共享的版本库执行拉回操作。也也许你(CVS)会觉得:command:`git fetch`或者:command:`git pull`和你的:command:`cvs update`命令更像吧。至于你的检出命令(:command:`cvs checkout`),实际上和我克隆命令(:command:`git clone`)很相似,只不过我的克隆命令不但创建了本地工作区,而且在本地还复制了和远程版本库一样的本地版本库。

CVS:为什么你的检入命令(commit)命令执行的那么快?

Git:是的,我的检入命令飞一般就执行完了,也是因为版本库就在本地。也许你(CVS)会觉得我的推送命令(:command:`git push`)和你的检入命令(:command:`cvs commit`)更相像,其实这是一个误会。如果我不做本地提交,是不能通过推送命令(:command:`git push`)将我的本地的提交共享给(推送给)其他版本库的。你(CVS)每一次提交都要和版本库进行网络通讯,而我可以在本地版本库进行多次提交,直到我的主人想喝咖啡了才执行一次:command:`git push`,将我本地版本库中新的提交推送给远程版本库。

CVS:我每一个文件都一个独立的版本号,你有么?

Git:每一个文件一个版本号?这有什么值得夸耀的?我听说你最早是用脚本对RCS系统进行封装实现的,所以你每个文件都有一个独立的版本控制,这让你变得很零碎。我听说某些商业版本控制系统也是这样,真糟糕。我的每一次提交都有一个全球唯一的版本号,这样不但是在本地版本库中是唯一的,和其他人的版本库也不会有冲突。

CVS:我能一次检出一个目录,你好像不能吧?

Git:所以我有子模组,以及repo等第三方工具,可以帮助我把一个大的版本库拆开多个版本库组合来使用啊。

CVS:我能添加空目录,你好像不能吧!

Git:是的,我现在还不能记录空目录。但是用户可以在空目录下创建一个隐含文件,并将该隐含文件添加到版本库中,也就实现了空目录添加的功能。你,CVS,目录管理是你的软肋,你很难实现目录的重命名,而目录重命名对我来说是小菜一碟。

9.2.2. CVS和Git命令对照

比较项目 CVS命令 Git命令
URL :pserver:user@host:/path/to/cvsroot git://host/path/to/repos.git
/path/to/cvsroot ssh://user@host/path/to/repos.git
  user@host:path/to/repos.git
  file:///path/to/repos.git
  /path/to/repos.git
版本库初始化 cvs -d <path> init git init [–bare] <path>
导入数据 cvs -d <url> import -m ... git clone; git add .; git commit
版本库检出 cvs -d <url> checkout [-d <path>] <module> git clone <url> <path>
版本库分支检出 cvs -d <url> checkout -r <branch> <module> git clone -b <branch> <url>
工作区更新 cvs update git pull
更新至历史版本 cvs update -r <rev> git checkout <commit>
更新到指定日期 cvs update -D <date> git checkout HEAD@’{<date>}’
更新至最新提交 cvs update -A git checkout master
切换至里程碑 cvs update -r <tag> git checkout <tag>
切换至分支 cvs update -r <branch> git checkout <branch>
还原文件/强制覆盖 cvs up -C <path> git checkout – <path>
添加文件 cvs add <TextFile> git add <TextFile>
添加文件(二进制) cvs add -kb <BinaryFile> git add <BinaryFile>
删除文件 cvs remove -f <path> git rm <path>
移动文件 mv <old> <new>; cvs rm <old>; cvs add <new> git mv <old> <new>
反删除文件 cvs add <path> git add <path>
工作区差异比较 cvs diff -u git diff
git diff –cached
git diff HEAD
版本间差异比较 cvs diff -u -r <rev1> -r <rev2> <path> git diff <commit1> <commit2> – <path>
查看工作区状态 cvs -n up git status
提交 cvs commit -m “<msg>” git commit -a -m “<msg>” ; git push
显示提交日志 cvs log <path> | less git log
逐行追溯 cvs annotate git blame
显示里程碑/分支 cvs status -v git tag
git branch
git show-ref
创建里程碑 cvs tag [-r <rev>] <tagname> . git tag [-m “<msg>”] <tagname> [<commit>]
删除里程碑 cvs rtag -d <tagname> git tag -d <tagname>
创建分支 cvs rtag -b -r <rev> -b <branch> <module> git branch <branch> <commit>
git checkout -b <branch> <commit>
删除分支 cvs rtag -d <branch> git branch -d <branch>
导出项目文件 cvs -d <url> export -r <tag> <module> git archive -o <output.tar> <tag> <path>
git archive -o <output.tar> –remote=<url> <tag> <path>
分支合并 cvs update [-j <start>] -j <end>; cvs commit git merge <branch>
显示文件列表 cvs ls git ls-files
cvs -d <url> rls -r <rev> git ls-tree <commit>
更改提交说明 cvs admin -m <rev>:<msg> <path> git commit –amend
撤消提交 cvs admin -o <range> <path> git reset [ –soft | –hard ] HEAD^
杂项 .cvsignore 文件 .gitignore 文件
参数 -kb 设置二进制模式 -text 属性
参数 -kv 开启关键字扩展 export-subst 属性