大约2周前 - 没有评论
Subversion 没有后悔药,就是说一旦完成向服务器的数据提交,就没有办法再追回(从客户端),只能在后续的提交中修正——回退或者修改等。
Git 非常神奇,拥有无数颗粒后悔药…
为什么Subversion 没有后悔药,而 Git 拥有呢。因为 Subversion 作为集中式的版本控制,不能允许个人对已提交的数据进行篡改,而Git 是分布式版本控制系统,代码库是属于个人,允许任意修改。Git 通过对提交建立数字摘要来保证提交的唯一性和不可更改性,通过版本库在多人之间的多份拷贝保障数据的安全性。
Git 丢弃最新的一个或几个提交
使用 git reset –hard 命令可以永远丢弃最新的一个或者几个提交。
丢弃最新的一个提交:
$ git reset –hard HEAD^
丢弃最新的两个提交:
$ git reset –hard HEAD^^
丢弃某一提交之后的改动
$ git reset –hard COMMIT-ID
Git 和 Subversion 修改提交说明
提交后如果对提交说明不满意,Git 可以使用命令 git commit –amend 修改提交说明。
Subversion 也可以修改提交说明,是通过修改提交的 svn:log 版本属性实现的:
不但可以修改最后一次提交的说明,并且可以修改历史提交的提交说明;
Subversion 修改提交说明是不可逆的操作,可能会造成提交说明被恶意修改;
Subversion 缺省关闭修改提交说明的功能。管理员在设置了提交说明更改的邮件通知后,才可以打开该功能
关于 Git 修改提交说明
Git 可以修改最后一次提交说明,并不是说不能修改历史版本的提交说明,只是修改最后一个版本提交说明拥有最简单的命令
Git 修改提交说明,会改变提交的 commit-id。即修改提交说明后,将产生一个新的提交
Git 可以通过 git reset –hard ,git commit –amend,git rebase onto 等命令来实现对历史提交的修改
使用 [...]
大约3周前 - 没有评论
Subversion 曾经骄傲的宣称,自己的分支是轻量级的,眨眼之间分支立现。但是说实话,Subversion的分支和里程碑,是 svn copy 命令的副产品,好像是折衷的产物。
Git 分支一出,无人敢于争风,信乎?
Subversion 和 Git 的分支/里程碑都是轻量的
轻量级分支/里程碑的含义是,创建分支/里程碑的复杂度是 o(1),不会因为版本库的愈加庞大而变得缓慢。在 CVS 中,创建分支的复杂度是 o(n) 的,导致大的版本库的的分支创建非常缓慢。
Subversion 轻量级分支的实现是通过 svn cp 命令,即带历史的拷贝就是创建快速创建分支和里程碑的秘籍。
Git 的轻量级分支和里程碑就是全球唯一的提交号的别名,其中分支对应的是 git 树状提交的分支顶极节点。
Git 的分支是完全隔离的,而 Subversion 则没有
分支本来就应该是相对独立的命名空间,这在 Git 中是没有问题的,一个提交只能发生在唯一的一个分支中,虽然提交可以通过 cherry-pick 被“挑选”合并到其他分支。
Subversion 的分支相当于目录拷贝,约定俗成是拷贝在 branches/ 目录下,目录之间的隔离完全靠着使用者的自觉自愿,谁也不能阻止在一个提交中同时修改不同分支中的数据。
Git 的里程碑是只读的,而 Subversion 仅凭约定俗成的自觉自愿
里程碑是对某个历史提交所起的一个别名,作为历史的标记,是不应该被更改的。
Git 完全遵守历史不可更改这一时空法则。用户不能向 git 的里程碑中提交,否则里程碑就不是标记,而成了一个分支。当然 Git 允许用户删除里程碑再重新创建指定到不同历史提交。
Subversion 的里程碑和分支一样,都是用 svn cp 的带历史的拷贝创建的,作为一个子目录而存在。约定俗成,svn 的里程碑要建立到 tags/ 目录下,要求不要在 tags/ 下的里程碑目录下进行提交。但是谁也阻止不了对未进行权限控制的里程碑的篡改。
Git 完备的里程碑和分支功能,另 Git 能完整克隆 SVN 版本库
很多分布式版本控制系统的分支功能是缺乏的,如 hg。因为像 hg/mercurial [...]
大约1月前 - 没有评论
不单单是 Subversion 的用户,还包括其他类型的分布式版本控制的用户,如 Hg 的使用者,都可能会对 Git 的 stage 或称为 index 的东西感到非常的陌生。
但是一旦你熟悉 Git 的 stage 的秉性,你就会喜欢上它。
关于 stage 概念
stage 是介于 workcopy 和 版本库 HEAD 版本的一种中间状态。通过索引文件 .git/index 可以找到 stage,因而 stage 有可以叫做 index。
stage 可以视作 Subversion 中的 changelist。即加入 stage 的变更在提交的时候提及到版本库,没有加入 stage 的变更则不会提交。Git 提供相应命令 (add,rm) 将工作区变更加入到 stage,也提供从 stage 中撤销变更的功能。参见下图:
古怪的 git add
其他的版本控制系统,也提供 add 命令,但是 add 命令仅仅是将未标记为版本控制状态的文件标记为添加状态,并在下次提交时入库。
而 git 的 add 自命令,除了对尚未版本控制的文件进行添加外,还对工作区的修改文件进行操作。命令 git [...]
大约1月前 - 没有评论
Subversion 可以将整个库检出到工作区,也可以将某个目录检出到工作区。对于要使用一个庞大、臃肿的版本库的用户,部分检出是非常方便和实际的。
但是 Git 只能全部检出,不支持按照目录进行部分检出。
那么这是为什么呢? —— Subversion 用户问道。
Git 的确没有部分检出,这并不是说只有将整个库克隆下来才能查看文件。有很多 git 工具,提供直接浏览git库的功能,例如 gitweb, trac 的 git 版本库浏览, redmine 的 git 版本库浏览。
Git 为什么没有部分检出?
Git 以及其他分布式版本控制工具,据我所知,都没有实现部分检出的功能,至少没有实现如下的部分检出:
想像中的完美版本控制系统(但并不存在):
可以对一个大的版本库(分布式)进行部分检出,检出的也是一个独立的小版本库(分布式)
小的版本库也可以被克隆
小的版本库中的提交可以 PUSH 到大的版本库中
小的版本库可以从大的版本库 PULL 相应目录的改动内容
这种版本控制系统不能在分布式版本控制系统中实现,我认为:
大的版本库和部分检出的小的版本库,无法保证提交ID的一致性
因为分布式版本控制系统的提交ID是整个提交信息的SHA1哈希值,大版本库的提交经过裁减——部分检出后,内容变了,提交 ID 也应该改变才对。
因为大的版本库和小的版本库(部分检出)的提交ID不一致,导致两个版本库的 PULL 和 PUSH 无法操作
即不知道哪些提交相互对应,造成两个版本库间的PULL和PUSH无法进行
分布式版本控制系统中,提交作为一个整体存在,一个提交由于部分检出而人为分拆,造成混乱,如何合并呢?
Git 为什么没有必要实现 svn export 的功能?
Subversion 有一条命令:svn export ,可以将 subversion 版本库的一个目录下所有内容导出到指定的目录下。而 git 却无法找到类似命令。为什么呢?
Subversion 需要 svn export 命令是因为该命令可以导出一个干净的目录,即不包含 .svn 目录(包含配置文件和文件原始拷贝)
Git 只在根目录存在一个 .git 目录,此外在各个子目录下不再有任何控制目录存在,因此无须通过另外的命令导出一个存脆的干净的原始文件目录
只需要用系统提供的文件/目录拷贝命令,即可以实现将干净的目录复制到指定的目录中
Git-submodule 可以实现版本库的模块化
如果说 git submodule 和部分检出的相似性,还不如说 [...]
大约1月前 - 4条评论
在使用 svnadmin dump, svnadmin load, svndumpfilter 等命令对 Subversion 版本库裁减,可真的不是 a piece of cake. 有很多技巧,窍门和陷阱。
这不,今天一个客户的电话,就涉及到了 svn 版本库裁减的好些问题:
svndumpfilter 命令后面的 include 或者 exclude 子语句,后面的多个路径用逗号分割可以么?
svndumpfilter 命令后面的 include 或者 exclude 子语句,后面的路径可以使用通配符么?如何使用?
svndumpfilter 命令涉及的路径非常多,在命令行写太复杂了,甚至可能超过 SHELL 对命令行长度的限制,该如何?
重新整理的版本库为什么有很多空的提交,说是为了占位之用?
重新整理后的版本库的路径可以改变么?
简要回答
客户的实际需求是将几十个GB的代码库,将某一个或某几个分支拆分出来形成新的版本库。但是在执行 svndumpfilter 后产生的过滤文件只有几十MB,而且在导入到新版本库后,版本库当中无内容,只有空的提交。
后来查看客户的命令,发现 svndumpfilter 命令的格式不正确,include 或者 exclude 子命令的参数是一个路径列表,这个路径列表应该用空格分隔,而不能用逗号。
还有:路径不支持通配符,因此需要一个一个的写,如果太多,可以放在一个文件中,用回车符分割。
在命令行中应该用空格分割包含或者排除的路径列表。看看 svndumpfilter 的这段代码:
if (subcommand->cmd_func != subcommand_help)
{
opt_state.prefixes = apr_array_make(pool, os->argc – os->ind,
sizeof(const char [...]
大约1月前 - 没有评论
有两种情况会创建 git submodule (git 子模组)
显性方式添加:使用 git submodule 命令将其他git库作为子目录添加,即子模组
隐性方式添加:使用 git add 添加,如果某个子目录本身是一个 git 库,就自动添加为子模组,不再递归添加该目录下面的文件
那么这两种方式添加的子模组有什么不同?子模组有什么副作用?如何删除模组呢?
两种方式添加模组,效果略有不同
隐性方式添加,看似直接将目录加入版本库,而实际上是加入一个和目录名同名的 submodule 条目;
显性方式添加,除了像隐性方式在 index/commit 中创建submodule 条目外,还会创建一个 .gitmodules 文件,也会在 .git/config 中创建相应记录。具体参见 git submodule 命令。
子模组的副作用
有时,并未意识到目录按照模组方式添加。例如在用 gistore 备份文件和目录时,当某个目录本身用 git 做了版本控制,就会以子模组的方式添加目录。
如何将子模组按照正常的目录形式添加到版本控制系统呢?用下面的方法做不到:
删除子模组的 .git 目录,即将子模组下的 git 版本库删除
当执行 git add 时报错:
fatal: Path ‘… …’ is in submodule ‘…’
那么,该怎么办呢?
如何删除子模组
使用 git 命令即可删除子模组
git rm –cached path/to/submodule
对于显性定义的子模组,还要删除 .gitmodules 文件和 .git/config 文件中的相关条目。
大约1月前 - 没有评论
Subversion 的全局版本号和 CVS 的每个文件都独立维护一套版本号相比,是一个非常大的进步。在看似简单的全局版本号的背后,是 Subversion 提供对于事物处理的支持,每一个事物处理(即一次提交)都具有整个版本库全局唯一的版本号。
Git 的版本号则更进一步,版本号是全球唯一的。也可以说是全宇宙唯一的。 Git 对于每一次提交,要将包括作者,提交内容等在内的信息整个作一个 SHA1 哈希,进而得到版本号。版本号得到一个 40 位十六进制字串。
什么?40位长的版本号?
全球唯一的版本号,您认为多长合适?
对于一个分布式的版本控制系统,每个人都可以独立创建提交,每次提交都形成一个新的版本号,每个版本号都不能够重复。使用摘要是解决唯一性的好方法,而当前来看使用 SHA1 是非常稳妥的。而 SHA1 就是一个 40位的十六进制的字串。
要是 SHA1 发生冲突了呢?即使真的发生冲突,还可以使用更加复杂的摘要算法,采用更长的摘要。如:SHA-224, SHA-256, SHA-384, SHA-512, …
SVN 的版本号是连续的,可以预判下一个版本号,而 Git 的版本号则不是
因为 subversion 是集中式版本控制,当然很容易实现版本号的连续性。Git 是分布式的版本控制而且 Git 采用 40 位长的哈希值作为版本号,每个人的提交都是各自独立完成的,没有先后之分(即使提交有先后之分,也由于PUSH/PULL的方向和时机而不同)。
有人可能用过 Hg (Mercurial),可能会说 Hg 虽然也用 SHA1 摘要,但是还同时提供一个递增的数字版本号哇,为什么 Git 没有?
我是这么认为的:
首先 Hg 的递增数字版本号不是全局或者全球唯一的,而只是在本地版本中存在的一个指向全球版本号的别名而已;
Hg 可以这么做,而 Git 没有这么做的原因是 Git 实现了真正的分支管理,而 Hg 版本库本身没有实现真正的分支管理;
Git 的版本号虽然不连续,但是是有线索的,即每一个版本都有对应的父版本(一个或者两个),进而可以形成一个复杂的提交链
Git 的版本号简化
如果每次需要引用使用版本号的时候,都使用 [...]
大约1月前 - 没有评论
关于 tg summary 速度改进,其实不是因为 tg summary 使用中发现的问题。tg summary 是在使用 topgit 最常用到的命令。该命令用于查看各个功能分支和版本库的同步关系。
一般情况下,不太容易受到这个小问题的干扰,但是如果存在近百个功能分支,就有问题了:
执行 tg create <TAB>,即在 tg create 命令后按下 Tab 键,想查看当前分支列表,会发现速度很慢,需要一秒钟甚至更长的时间相应。可是用命令 git co 按下命令,瞬间就提供分支名的自动补齐
tg depend 命令也存在类似问题
tg patch 命令如果按下 Tab 键,也存在类似问题
…
追根溯源,找到原来是 tg summary -t 命令的性能问题。命令补齐实际上是调用的 tg summary -t 命令来获取 topgit 分支的。
看看改进前和改进后,执行 tg summary -t 命令的效率:
改进前:
$ time tg summary -t >/dev/null
real 0m1.799s
user 0m0.580s
sys [...]
大约1月前 - 没有评论
在 Bash 的 Shell 环境下,Linux 命令的命令补齐非常强大。
首先,要确保安装了 bash-completion 包
$ dpkg -l bash-completion
||/ 名称 版本 简介
+++-=================-=========-==========================================
ii bash-completion 1:1.1-3 programmable completion for the bash shell
还有确认 Bash 的配置文件 /etc/bash.bashrc 已经开启了命令补齐,如下:
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
Topgit 也提供 Bash 下的命令补齐,相应的脚本在目录 /etc/bash_completion.d/ 下
但是 Topgit 的命令补齐有一个问题,就是在执行 tg depend 命令,通过敲击 TAB 键获取分支列表或者自动补齐分支名称时出现异常!
$ tg depend <TAB> bash: __git_find_subcommand: command not found
add
命令补齐报错!这是如何产生的呢?如何解决呢?
TopGit 命令行补齐的Bugfix
如前所述,理解了bash命令补齐的原理,就能很容易解决这个问题。
Bash 命令补齐,需要在 [...]
大约1月前 - 没有评论
SVN 用户对 Git 的不好的体验,可能大多来自于两者命令集差异很大,不兼容,感觉非常不习惯。
这其中的一部分原因是因为 SVN 和 Git 的原理不同,分属不同阵营——集中式和分布式版本控制;另外一个重要的原因,可能就是 Linus Torvals 痛恨 CVS,而且 Torvals 曾经说过的很有争议话,就是他认为 SVN 也是一个失败。所以,Torvals 设计的 Git 当然要特立独行了。
不过…
由于原理不同,导致 SVN 和 Git 不兼容的命令
svn checkout 和 git clone
这两个命令,都是首次从其他版本库创建本地拷贝时运行的命令,都是只需要执行一次就可以的命令。
“svn checkout” 就是检出,很形象的比喻,subversion 就是要从服务器的版本库建立本地的工作拷贝(工作区)。检出之后的本地拷贝和版本库有着千丝万缕的联系(每个子目录下的 .svn 目录中的 entries 文件都会标记版本库的地址),本地提交都要上传到服务器版本库中完成。
“git clone”就是克隆,也是非常形象的比喻。作为分布式版本控制系统,通过克隆创建的本地版本库和远程版本库一模一样,没有谁比谁更好。克隆之后的本地版本库和源版本库有着一丝联系(在 .git/config 中配置 remote版本库的URL),这一丝联系,无非是为了不定期的双方分享改动而已。
svn update 和 git checkout, git pull
真的很难说和 “svn update” 对应的 git 命令,是 git checkout? 还是 git pull?
“svn update” [...]