Subversion 用户眼中的 Git (5): 没有部分检出
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 和部分检出的相似性,还不如说 git submodule 就是 svn:externals 翻版。
就是说 git submodule 可以将各个小的 git 版本库集合为一个大的版本库。如果不需要整个大的版本库的话,可以仅仅克隆某个小的 git 库。
Git-svn:集合 svn 的部分检出和 git 的便利
Git-svn 是 Subversion 的最佳伴侣,可以用 Git 来操作 Subversion 版本库。这带来一个非常有意思的副产品——部分检出:
- 可以用 git-svn 来对 Subversion 代码库的任何目录进行克隆,克隆出来的是一个git版本库
- 可以在部分克隆的版本库中用 Git 进行本地提交
- 部分克隆版本库中的本地提交可以提交到上游 Subversion 版本库的相应目录中
还没有评论。
339还没有引用。
TopGit的使用技巧 (3)
大约1周前 - 没有评论
含有分支依赖且产生冲突的update操作
创建一个Git版本库demo1
$ git clone demo1
$ cd demo1
$ vi sum.py #编写一个求和的函数
$ cat sum.py
def sum(a,b):
return a + b
print “sum(1+2)=”,sum(1,2)
$ git add .
$ git ci -m “init respoitory”
创建一个TopGit分支
$ tg create t/hack1
$ vi sum.py #修改sum.py的形参
$ cat sum.py
def sum(num1,num2):
return num1 + num2
print “sum(1+2)=”,sum(1,2)
$ git ci -am “modify sum function’s parameter”
$ tg summary
> t/hack1 [PATCH] t/hack1
$ git rev-parse master top-bases/t/hack1 [...]
TopGit的使用技巧 (2)
大约1周前 - 没有评论
含有冲突的update操作
创建一个Git版本库demo1
$ git clone demo1
$ cd demo1
$ vi sum.py #编写一个求和的函数
$ cat sum.py
def sum(a,b):
return a + b
print “sum(1+2)=”,sum(1,2)
$ git add .
$ git ci -m “init repository”
创建一个TopGit分支
$ tg create t/hack1
$ vi sum.py #修改sum.py的形参
$ cat sum.py
def sum(num1,num2):
return num1 + num2
print “sum(1+2)=”,sum(1,2)
$ git ci -am “modify sum function’s parameter”
$ tg summary #查看当前TopGit分支的状态
> t/hack1 [PATCH] t/hack1
$ git rev-parse master top-bases/t/hack1 t/hack1
905d75e8cb4392149b0c74b03c53e5e6fdcac3cf
905d75e8cb4392149b0c74b03c53e5e6fdcac3cf
ebd8259b997ef79a423fd80be0748be48db4bdd2
修改master,致使 [...]
TopGit的使用技巧 (1)
大约1周前 - 没有评论
TopGit是基于分布式版本控制工具Git的一个轻量级的分支及补丁管理工具。使用TopGit可以很好的管理基于分支的开发模式。尤其对于二次开发人员来说,TopGit就是他们的杀手锏。
首先原型版本的代码用Git初始化成Git库,然后每给原型增加一个功能或者修改一个Bug都用TopGit创建一个分支(tg create t/feture1 或者 tg create t/bug1-fix),这样每一个功能或者Bug修复都是一个TopGit分支。当原型版本升级后,我们只需要切换到原型分支,即master分支 (git co master),然后导入原型的新版本代码,最后将TopGit的分支一个一个迁移到新的原型之上,就完成了一次版本的升级。真的很有用啊,这个东东!
下面介绍一些TopGit的使用技巧,尤其是关于分支update的问题
没有冲突的update操作
#创建一个Git版本库demo1
$ git clone demo1
$ cd demo1
$ vi sum.py #编写一个求和的函数
$cat sum.py
def sum(a,b):
return a + b
print “sum(1+2)=”,sum(1,2)
$ git add .
$ git ci -m “init repository”
#创建一个TopGit分支
$ tg create t/hack1
$ vi sum.py #修改sum.py的形参
$ cat sum.py
def sum(num1,num2):
return num1 + num2
print “sum(1+2)=”,sum(1,2)
$ git ci -am “modify sum function’s parameter”
$ tg summary #查看当前TopGit分支的状态
> [...]
Subversion 用户眼中的 Git (8): SVN没有后悔药,git有好多
大约3周前 - 没有评论
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 等命令来实现对历史提交的修改
使用 [...]
Subversion 用户眼中的 Git (7): 完全不同的分支和里程碑的实现
大约4周前 - 没有评论
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 [...]
Subversion 用户眼中的 Git (6): stage
大约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 版本库整理实战 中为用户提供的 Subversion 版本库整理宝典 ,在客户那里没有奏效。通过几个邮件的往来,决定还是专门写一个博客,因为通过博客后面的评论来回复,编辑功能太弱,要写 HTML,所以以文章形式汇总一下。
客户目前遇到了两个问题:
一个是导入到新版本时提示目录不存在
另外一个就非常诡异,错误输出是:
svnadmin: 转存流在“Out of memory – term”包含错误头部(没有“:”)
第一个问题很好解决,第二个问题可真是一个大麻烦。
关于第一个问题的回复
我在博客中的例子和你的不一样,是因为我的例子中,svndumpfilter 包含是整个
branches 目录(是一级目录),而你的操作是二级目录。
我的例子中导出文件是一级目录,可以直接导入,因为根 / 已经存在。
你的例子是二级目录,不存在 branches,在创建空的版本库后,需要创建一级子目录
然后再导入。
即创建完毕空版本库(svnadmin create test)后,执行
svn mkdir file:///path/to/repos/branches
诸如此类。。。
如果您在导入时,提示已经存在了某个目录或文件,您则需要执行
svn rm file:///path/to/repos/path/to/conflict
删除已经存在的目录或者文件。
关于第二个问题的回复
第二个问题,我的第一个判断是内存溢出,可能是用户的库太大了。于是我回复客户:
看来是导出文件太大了,导致内存不足。
(当时的这个分析不太准确。实际上是导出的时候出现了异常。导致导出文件的格式不正确,出现了不包含冒号的一个Header)
解决方法是:在用 svnadmin dump 的时候,使用 -r X:Y 参数,分批导出。
例如第一次先导出 1000 个提交,第二次再导出 1000 个提交。
注意:在第二次之后的导出时,要使用 –incremental 增量导出。
我还要求他们在执行命令时最好设置环境变量 LC_ALL=C,以便产生不那么别扭的错误输出。
客户的回复大意如下:
svn库大小为21G(压缩后的gz文件大小,不是原始的目录文件大小,最新版本号为428113),整个库dump出来的文件夹大概在50G左右,dump时间也大概在18个小时左右。但是如果我们指定了一个版本号区间,248253:424935,指定版本区间的dump时间远远大于整个库的dump时间,大概花了53个小时才完成,dump的文件居然有200多G。 dump后进行filter过滤后的文件才1.5G左右,这种文件的大小应该不会引起内存溢出的。(因为服务器内存足够)
当设置为英文语种后,错误输出为:
svnadmin: Dump stream contains a malformed header (with no ‘:’) at ‘Out of memory – term’
在load的过程中,整个机器的内存使用都不超过50%,机器的物理内存绝对足够。
Dump [...]
Subversion 版本库整理实战
大约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 [...]
删除 git submodule (git 库子模组)
大约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 文件中的相关条目。
Subversion 用户眼中的 Git (4): 全局版本号和全球版本号
大约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 的版本号简化
如果每次需要引用使用版本号的时候,都使用 [...]