Git:我的提交是原子提交。每次提交都对应于一个目录树(树对象)。因为我的提交ID是对目录树及相关的提交信息建立的一个SHA1哈希值,所以可以保证数据的完整性。
CVS:我承认这是我的软肋,一次错误或冲突的提交会导致部分数据被提交,而部分数据没有提交,版本库完整性被破坏,所以人们才设计出来 Subversion(SVN) 来取代我。
Git:我的分支和里程碑管理非常快捷。因为我的分支和里程碑就是一个记录提交ID的引用,你的呢?
CVS:你怎么又提到别人的痛处了!我的分支和里程碑创建速度还是很快的,...嗯... , 如果在版本库中只有几个文件的话。当然如果版本库的文件的很多,创建分支、里程碑创建就需要花费更多的时间。有些人对此忍无可忍,于是设计出 SVN 来取代我。
Git:其实我不用里程碑都没有关系,因为每一个提交ID就对应于唯一的一个提交状态。
CVS:这也是我做不到的。我没有全局版本号的概念,每一个文件都通过单独的版本号记录其变更历史,所以人们在使用我的时候必须经常地用里程碑 (tag)对我的状态进行标识。还需要提醒一句,如果版本库中文件太多,创建里程碑是很耗时的,因为要一一打开每一个版本库中的文件,在其中记录里程碑和 文件版本的关系。
Git:我的工作区很干净。只在工作区的根目录下有一个 .git 目录,此外再无其他辅助目录或文件。
CVS:我要在工作区的每一个目录下都放置一个 CVS 目录,这个目录下有个
Entries 文件很重要,记录了对应工作区文件的检出版本以及时间戳等信息。这样做的好处是可以将工作区移动到任何其他磁盘和目录,依然可以使用,甚至我可以将工作区的一个子目录拿出来,作为独立的工作区。
Git:我也可以将工作区移动到其他磁盘,但是要保证工作区下的 .git 目录和工作区一同移动。也不可以只移动工作区下的一个目录到其他磁盘或目录,那样的话移出的目录就不能工作了。
Git:我的网络传输效率很高。在和其他版本库交互时,对方会告诉我他有什么,我也知道我有什么,因为只对缺失对象的打包传输,所以效率很高而且能够显示传输进度。
CVS:这一点我不行。因为我本地没有文件做对照,所以我在传输的时候不可能做到增量传输。
Git:我甚至可以不需要网络,因为我在本地拥有完整的版本库,几乎所有操作都是在本地完成。
CVS:我的操作处处需要网络,如果版本库是在网络中其他服务器上的话。如果网速比较慢,查看日志、查看历史版本都需要花费很长时间等待。
CVS:你怎么没有更新(update)命令?还有你为什么老是要执行检出命令(checkout)?对我而言,检出命令只在工作区创建时一次完成的。
Git:你的检出命令(checkout)是从远程版本库服务器获取数据完成本地工作区的创建,版本库仍然位于远程的服务器上。你 的更新(update)命令执行的很慢对么?之所以你需要执行更新命令是因为你的版本库在远程啊。别忘了我的版本库是在本地,我的每一步操作工作区和版本 库都是同步的,所以更新操作就没有存在的必要了。而我的检出(checkout)操作是将本地版本库的数据检出到本地工作区,用于恢复本地丢失的文件或错 误改动的文件,也用于切换不同的分支。我也有一个和你的更新(update)操作类似的比较耗时的网络操作命令叫做 git fetch 或 git pull,这两个操作是从别人的版本库获取他人改动。一般使用我(Git)做团队协作的时候,会部署一个集中共享的版本库,我就用这两个命令(git fetch 或 git pull)从共享的版本库执行拉回操作。也也许你(CVS)会觉得 git fetch 或者 git pull 和你的 cvs update 命令更像吧。至于你的检出命令(cvs checkout),实际上和我克隆命令(git clone)很相似,只不过我的克隆命令不但创建了本地工作区,而且在本地还复制了和远程版本库一样的本地版本库。
CVS:为什么你的检入命令(commit)命令执行的那么快?
Git:是的,我的检入命令飞一般就执行完了,也是因为版本库就在本地。也许你(CVS)会觉得我的推送命令(git push)和你的检入命令(cvs commit)更相像,其实这是一个误会。如果我不做本地提交,是不能通过推送命令(git push)将我的本地的提交共享给(推送给)其他版本库的。你(CVS)每一次提交都要和版本库进行网络通讯,而我可以在本地版本库进行多次提交,直到我 的主人想喝咖啡了才执行一次 git push,将我本地版本库中新的提交推送给远程版本库。
CVS:我每一个文件都一个独立的版本号,你有么?
Git:每一个文件一个版本号?这有什么值得夸耀的?我听说你最早是用脚本对 RCS 系统进行封装实现的,所以你每个文件都有一个独立的版本控制,这让你变得很零碎。我听说某些商业版本控制系统也是这样,真糟糕。我的每一次提交都有一个全 球唯一的版本号,这样不但是在本地版本库中是唯一的,和其他人的版本库也不会有冲突。
CVS:我能一次检出一个目录,你好像不能吧?
Git:所以我有子模组,以及 repo 等第三方工具,可以帮助我把一个大的版本库拆开多个版本库组合来使用啊。
CVS:我能添加空目录,你好像不能吧!
Git:是的,我现在还不能记录空目录。但是用户可以在空目录下创建一个隐含文件,并将该隐含文件添加到版本库中,也就实现了空目录添加的功能。你,CVS,目录管理是你的软肋,你很难实现目录的重命名,而目录重命名对我来说是小菜一碟。
比较项目 |
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 属性 |