免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: send_linux
打印 上一主题 下一主题

[其他] 版本控制之道---Git时代已经来到?(获奖名单已公布-2012-10-30) [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
51 [报告]
发表于 2012-10-11 16:34 |只看该作者
回复 48# OwnWaterloo


    擦,我就是被你害了!仔细看看你的拼写!darcs不是dracs!!!


svn似乎也是用patches进行管理的。只是做的不好。但是这是无可避免的。我问你。patches最重要的是什么?是历史!至少是单个的前向历史。patches最重要的就是你得知道对谁patch,所以你不可能只保存patch,你必须保存patch和一个指针,说明patch对谁进行!

所以,git明显是做得到的。假设有个历史A->B->C->D,你只需要rebase就能够merge B和C,得到A->C'->D,这个过程和darcs是一样的,即如果要得到A->C‘->D,darcs也必须构建一个新的patchC',这个patch是patch B和patch C merge得到的,不可能直接对Apatch掉C,很可能会冲突,并且丢掉了B的修改状态。

而且,patch本质上是对snapslot的一个模拟,只是一个实现问题。从这一点讲,保存snapslot和patch没有区别。git的内部对于强相关性的object也是用patch来维护的。这只是一个优化策略,本质上还是snapslot。

所以如果仅仅是你说的这一点,我觉得对git没有优势。git完全可以模拟这种行为,工作量是一样的——注意如果是patch,实际上你操作的仍然是个链表,你仍然需要遍历patch,观察patch的过程,并对patch进行合并,这和rebase的原理是一样的。根本不存在git需要很多工作量而darcs O(1)就能做到。最多只能说darcs对这种应用给予了支持。问题是git同样有支持——rebase,然后commit到一个新的分支,然后clone这个新的分支形成一个repo,完全一样的。

顺便说说push stash,stash有commit message?我怎么不知道?我只要共享一个状态,我不需要这个状态有commit-message。就是说,我本地有一个状态,一些文件add了,另一些modify了没record,我希望共享这个临时状态给别人,别人建立一个新的分支,同样一些文件add了,另一些modify了,就是这样。所谓同步

论坛徽章:
6
CU大牛徽章
日期:2013-03-14 14:14:08CU大牛徽章
日期:2013-03-14 14:14:26CU大牛徽章
日期:2013-03-14 14:14:29处女座
日期:2014-04-21 11:51:59辰龙
日期:2014-05-12 09:15:10NBA常规赛纪念章
日期:2015-05-04 22:32:03
52 [报告]
发表于 2012-10-11 16:48 |只看该作者
shuge_guet 发表于 2012-10-11 16:32
1.团队使用git,但少部分顽固派还在使用svn,个人使用git

选择git无它,就是想怎么搞就怎么搞,不管什么 ...


这个头像很吊,这个理由也很吊,想怎么搞就怎么搞

论坛徽章:
0
53 [报告]
发表于 2012-10-11 16:56 |只看该作者
回复 52# wang290

就像搞女人一样,想怎么搞都行。
svn的目录像女人的裹脚布一样,又乱又臭
svn提交记录也相对丑陋,git的看起来就爽点

git的分享也很方便,svn用过一段时间,还不知道怎么弄

   

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
54 [报告]
发表于 2012-10-11 16:58 |只看该作者
本帖最后由 starwing83 于 2012-10-11 17:04 编辑

回复 48# OwnWaterloo


    或者你想要的,其实就是在历史中,剔除掉B制造的所有修改?这样?

这样git也做得到——rebase,得到A<-B的patch,然后apply B,apply C,然后reverse patch那个A<-B的patch。问题是这安全么?这很可能造成patch失败的。git可以考虑做一个这样的功能,这一点都不难,只是容易冲突,比如可以在rebase的时候直接ERASE B,工作量是比darcs的大一点点,但是不是特别多。事实上,也就是要改掉之后所有的snaplslot而已。而且,因为snapslot本身是可以以patch方式存储的,所以这种更改代价很小,甚至可以做到和darcs完全一致的效率。

退一万步说,git里面已经有完全模拟darcs行为的机制了,即diff-object。两个object很相像在库里可以仅仅存储diff,如果我们规定一个新的格式,要求就得存储diff,那么git完全可以改造成跟darcs完全一致的特性。git的原理——content-addressable filesystem——本身对版本控制来说是个元原理,就跟图灵完全一样,我想不到有什么是做不到的。

darcs的缺陷在于得到一个working directory很困难,需要对working directory进行逆patch等等操作。这些也不是问题。完全可以在git的基础上实现darcs的任何特性。但是也不可避免地带来了复杂性。

我觉得git现在的snapslot管理方式已经很好了。你说的功能——无论是直接忽视掉B,还是去掉B的记录——都在目前的框架下做得到。而且做这个事情毕竟不是很频繁。git的框架为频繁的操作提供了速度,我觉得这比darcs好。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
55 [报告]
发表于 2012-10-11 17:11 |只看该作者
starwing83 发表于 2012-10-11 16:34
擦,我就是被你害了!仔细看看你的拼写!darcs不是dracs!!!

我的错。。。 但两种拼写都没有丑陋女人的意思啊。。。


starwing83 发表于 2012-10-11 16:34
顺便说说push stash,stash有commit message?我怎么不知道?我只要共享一个状态,我不需要这个状态有commit-message。就是说,我本地有一个状态,一些文件add了,另一些modify了没record,我希望共享这个临时状态给别人,别人建立一个新的分支,同样一些文件add了,另一些modify了,就是这样。所谓同步。

  1. mkdir repo
  2. cd repo
  3. git init
  4. touch a.txt
  5. git add a.txt
  6. git commit -m init

  7. echo 12 >> a.txt
  8. git stash

  9. git log -p -m $(cat .git/refs/stash)
复制代码
继续研究吧。。。 有成果了顺带分享一下。。。



starwing83 发表于 2012-10-11 16:34
svn似乎也是用patches进行管理的。只是做的不好。但是这是无可避免的。我问你。patches最重要的是什么?是历史!至少是单个的前向历史。patches最重要的就是你得知道对谁patch,所以你不可能只保存patch,你必须保存patch和一个指针,说明patch对谁进行!

什么指针? parent指针? patch可没有这东西。 这是git以及年代顺序才有的概念。

确实是有历史,不能任意apply patch。但git的历史不仅仅是这样。例如。
A

  1. + 1
  2. + 2
  3. + 3
  4. +...
  5. + 7
复制代码
B

  1. - 6
  2. + six
复制代码
C

  1. - 3
  2. + three
复制代码
这3个patch本身被强制的顺序只有 A 先于 B和A先于 C。 至于B与C谁先谁后是无所谓的。
而git会强制这样一个顺序, 不妨设是A <- B <- C。 那么想抽出一个 A <- C, git就得重写C的历史了, 只能做到 A <- C'


starwing83 发表于 2012-10-11 16:34
所以,git明显是做得到的。假设有个历史A->B->C->D,你只需要rebase就能够merge B和C,得到A->C'->D,这个过程和darcs是一样的,即如果要得到A->C‘->D,darcs也必须构建一个新的patchC',这个patch是patch B和patch C merge得到的,不可能直接对Apatch掉C,很可能会冲突,并且丢掉了B的修改状态。


不一样。 确实会存在冲突, 比如前面的先B(或C)然后A。 这是git与darcs都无力的。
但也有不冲突的情况, 比如前面的先A, 然后B(或C)。 darcs就给力了。
在这种情况下 darcs没有patchC' 这回事。 它的B和C就只依赖A, B和C之间没有顺序依赖。 你不能以git的思路去想darcs。。。

至于丢B的修改状态。。。 那不叫丢。。。 那叫有意识地排除这个patch。。。

比如notes的例子。 自然将clojure的notes单独抽取出来, 这些patches之间会存在一些强制性的顺序。
但它们肯定和其他notes无关, 是故意丢掉其他notes, 以单独整理出一份只和clojure相关的仓库。 是故意地(并且没有冲突地)这样做的。



starwing83 发表于 2012-10-11 16:34
所以如果仅仅是你说的这一点,我觉得对git没有优势。git完全可以模拟这种行为,工作量是一样的——注意如果是patch,实际上你操作的仍然是个链表,你仍然需要遍历patch,观察patch的过程,并对patch进行合并,这和rebase的原理是一样的。根本不存在git需要很多工作量而darcs O(1)就能做到。最多只能说darcs对这种应用给予了支持。问题是git同样有支持——rebase,然后commit到一个新的分支,然后clone这个新的分支形成一个repo,完全一样的。

darcs肯定不会是O(1)就做到, 实际上它就是因此有性能问题。。。
但它的patch没有年代顺序, 年代顺序的修改不会影响到它们的hash值, 它们都可以被共享。
而git会强制一个年代顺序, 并且会影响hash值。 A <- B <- C 与 A <- C <- B 中的B,C就不是相同的。

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015年亚洲杯之朝鲜
日期:2015-03-13 22:47:33IT运维版块每日发帖之星
日期:2016-01-09 06:20:00IT运维版块每周发帖之星
日期:2016-03-07 16:27:44
56 [报告]
发表于 2012-10-11 17:22 |只看该作者
本帖最后由 blackold 于 2012-10-11 17:24 编辑

正则是时刻都在使用的工具,VCS 的使用没有正则那么频繁,但也天天使用。

其实,我没有项目开发等良好的"IT"环境。对于计算机技术,与其说是工作的需要,倒不
如说更多的是自己的兴趣爱好,VCS也是如此。

刚刚使用电脑时,根本不知道VCS为何物。

使用电脑时间长了,经常会碰到类似的事情:

突然需要以前的合同,而不是现在的合同,而当前文件只保存了最新的合同,以前的合同
又没有备份……

辛辛苦苦做了个方案(比如项目方案,或者设计的图片),领导一看,不喜欢,一拍脑门说
如何如何改。于是你在没有备份现有方案(删掉了)的前提下,又重新按照领导的意思做了
新方案。过段时间,领导心血来潮,又说还是以前的方案好,用以前的方案吧。傻眼了吧。

……

诸如此类的事情,我想大家都碰到过不只一次。

要是当时备份文件了就好了。于是,每次要做新方案时都要提醒自己备份旧方案——使用最
原始的copy来备份^_^,以备不时之需。

但是,纯手工的东西也不见得好^_^,太累了,总有忘记备份的时候。要命的是,备份多了,
你自己都搞不清楚哪是哪——比没备份可能还惨。

要是有一个这样的管理系统就好了。

期间也碰到过"版本控制"这个术语,只是感觉那是很高级很玄的东西,也许与某个具体的
软件版本有关,也许是出版社弄的什么玩意^_^。我不开发软件,也不搞出版,版本控制与
我有什么关系?

又过了几年,才了解到 VCS 就是这样的系统。早就有了,只是自己孤陋寡闻罢了。

使用 VCS 可以轻松解决上述烦恼。你可以随时将文档恢复到以前某个时间点的状态——VCS
是一台时间机器,你可以放心修改文档,直到觉得满意再提交,不用担心数据丢失。

当然 VCS 的功能远不只于此。

最初接触、现在还经常使用的是SVN。觉得SVN比较正统、简单,容易上手。我用SVN来管理
所有的文档,“一切都处于版本控制之下”。日常使用的命令有:

svn st -u
svn diff
svn ci
svn up

对于大多数人来说,SVN 足够了。

后来又遇到了 git,据说很强大更自由。又开始玩git了^_^。

一开始,觉得 git有点古怪,不正统,难登大雅之堂——也许与我先使用SVN有关吧。

慢慢玩多了,觉得git真的强大,拥有一些 SVN 所没有的性能,同时也慢慢习惯了 git 的
工作方式。

印象最深的是 git 的签出是一个完整的版本库(SVN的签出只是一个WC)。一旦签出,你就
拥有一个完整版本库,可以随时随地编辑你的文档,而不受网络限制。所以,git 称为克
隆(相应的命令为clone)。这是我最喜欢 git 的原因之一。

至于速度,暂时没有明显的感觉。因为我的应用都是小规模的。

关于学习 VCS,我的经验就是——使用是最好的学习方式。一旦开始,你就觉得并没有
想象的那么困难。重要的是,你会不时体验到学习的乐趣。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
57 [报告]
发表于 2012-10-11 17:23 |只看该作者
回复 54# starwing83

没有逆patch。。。 working tree = set of patch。。。

至于我想要的。。。  就是git管理一些依赖很松散的东西不给力。。。 比如notes。。。
clojure的noteA —— 或者说每一个note —— 只应该依赖它之前的状态,而不应该依赖整个提交历史。
我希望能从里面挑选出若干修改(当然肯定是要满足必须的依赖), git里面就没有内建的支持。


clojure的notes还不多, 我可以自己从零开始,一个一个cherry-pick,构造出一个只有clojure的notes的仓库。 这工作量都还不大。
问题是。。。 要从原来的仓库里删除所有和clojure相关的 —— 以后和clojure相关的我就单独写在前面说的那个仓库了 —— 其实工作量也还是不大,可以和前面那个步骤一起完成。。。
但整个仓库就完全变了, 很多commit都被重写了, 有不同的hash值。
如果有人要拉取。。。 完蛋了。。。


而如果是darcs, 它本来管理的就只是一个一个patch。 分离为2个仓库的话, 这些patch都是不变的, 都有相同的hash值。
变的只是从原来的1个set变成了2个set。 而且也不是真的分离为2个仓库, 还是一个仓库, 有现在有2个(或者3个如果不删除原来那个的话) set。
如果有人要拉取, 需要的仅仅是2个新的set。
从patch中挑选合适的, 构造出一个新的set比git要容易。


说图灵完备什么的就没意思了。。。  反正darcs是开源的, 用C写一个理论上也是做得到的。。。 就是做了(可以立马用)与没做的区别。。。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
58 [报告]
发表于 2012-10-11 17:31 |只看该作者
回复 55# OwnWaterloo


   

很显然darcs必须记录一个顺序,至少是patch的顺序。可能每个patch不必记录一个前向的状态,但是这是不负责任的,很可能patch失败,换言之,darcs完全把责任转嫁给做事的人了。

一个好的方案是产生patch的时候还是带一个前向状态,但是是根据现在的历史树记录的一个状态集合。这样可能计算量会很大,但是绝对是安全的。

如果不产生失败,git不可能无力,git也可以做到,我前面说了,rebase加一个操作叫ERASE,方案就是我说的,计算A->B的patch,然后在C的时候reversed patch,然后提交给C'。

另外,你是不是搞混了某个东西……按照你的意思,要么:

1. darcs的一个commit记录的是一堆patch集合。
2. darcs的一个commit直接记录了这一堆的patch。

我猜想是前者,commit记录的是patch集合,而不是单个的patch。否则你无法组合这些patch。问题是:

1. 无论是git还是darcs,都有两个不同的层次:commit物件,和blob物件(对darcs来说是patch),blob的hash肯定不会变,而commit很小,变了又如何?rebase慢么?
2. 如果修改历史,darcs的commit对象依然得变。那么hash值也会变。这个变有两个含义:
   - 如果darch的最终历史是独立记录的,比如说一个记录patch的列表(即上面的2),每个分支都是这么一个列表,那么不错,patch本身不变,但是产生一个新的分支就是产生一个新的列表,本质上这个列表对应的是git的commit——大家都是O(n)的!都得变。
   - 如果darch和git一样,采用链表记录历史(即上面的1),那么这个链表的每个节点肯定得变——因为前向的节点变了!

换言之,对于一个版本控制工具来说——记录单线历史永远是必要的,不然根本不叫版本控制工具了。唯一问题是用什么方式记录。上面的分析表明,无论darcs用什么方式记录,最终的可能性也只是性能和git相同,不可能比git更好,git的元原理表明了,和版本控制工具相关的一系列操作,不可能有性能比git更高的存在。最多git在常数上操作会更加复杂,仅此而已。

现在到了提问时间,上面都是我的猜测,肯定有一个符合实际,但是我不知道是哪个,所以我现在想知道:

1. darcs的一次提交记录的到底是什么?是针对所有被修改文件的patch的列表,还是一个单个的patch包含了本次commit的所有修改?
2. darcs的一个commit,是否包含parent?即,这个commit知不知道自己是在什么基础上提交的?
3. 如果不包含,darcs是否有比commit更高一级的object?即history object?这个对象记录了一系列的commit?

如果三个问题的答案如我所料,那么git做成darcs的样子完全不困难——commit做成独立对象,不记录parent,创建history对象,记录commit列表。commit。那么立刻就是darcs类型了。如果要完全一样:commit不记录tree,而记录patch-blob的hash-list,就完全一致了。

问题是,这样必要么?这么做,是的,方便降低了提取独立修改集合的常数,双方的rebase其实都是O(n)的!但是代价呢?代价是创建分支的代价大大提高了——分支需要拷贝整个history object,从O(1)变成了O(n)!而历史通常是很大的!这是得不偿失的结果!还不如rebase的时候直接修改commit object,修改hash,因为rebase的操作远远不如branch频繁,这么做能完成darcs的所有功能,而且平时的效率更高!

为了减少一个不常用的操作的常数(复杂度还没变),将一个常见操作的复杂度由O(1)提高到了O(n),这不划算吧?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
59 [报告]
发表于 2012-10-11 17:40 |只看该作者
回复 54# starwing83

呃,不说notes。。。
就假设是个网站。。。  一人写html,css, 一人写js, 一人写clojure(为了保证相互间没有依赖, 例子选得极端了点。。。)

他们(HD, JD, CD, D代表developer)本地各自有一个仓库HL、JL、CL(L代表local)。
服务器上有4个仓库, 其中3个是他们负责推进去的, 叫HR, JR, CR(R代表remote)。 另一个是合并用的叫REPO, 就是真正的成品。
需要的工作就是HD, jD, CD:
1. REPO拉取
2. 往本地H,J,C提交
3. 将尽量干净的patch往HR, JR, CR推

一个人负责从HR, JR, CR中挑选出合适的,加入到REPO。(显然从某个XR中的挑选是有顺序的)

如果working tree的定义就是set of patches, 就很适合这种工作流了。


而如果是git, 要么就要进行很多rebase的工作。。。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
60 [报告]
发表于 2012-10-11 17:44 |只看该作者
回复 57# OwnWaterloo


    我懂了,你怕的其实就是commit变了别人一拉就完蛋是吧……那这无解,这是高效的必然问题。见上楼分析。

我说的逆patch,是说可以在rebase的时候做!比如说,A->B->C->D

现在rebase。

先apply A,commit,

然后apply B,这时不commit,反而git-diff计算差分,但是是反过来计算的(计算如何从B到A,而不是如何从A到B)

然后apply C,然后apply刚才计算的那个差分,相当于undo了B的操作,注意这可能很麻烦,因为C的修改有可能导致B的undo失败。但是在这种情况下,直接去掉B照样会导致C的patch apply失败,git和darcs都无解。

然后commit。

最后apply D,commit(注意依然可能失败,因为可能是在B的基础上commit的)

最后得到了A' <- C' <- D',完成。

这是在历史上提取。在目录上提取也简单。本质上基于patch的管理方式(包括SVN)提取目录历史都简单(SVN的历史就是基于目录的),darcs简单也就正常了。你可以遍历所有commit,然后将修改了notes那个tree的所有commit集合起来rewrite,最后得到一个新的branch,里面所有的操作都是对那个note的修改。

但是前提是你打算分仓库,这样才有意义。我个人建议也是分仓库就完了,那就完全没问题了。但是如果打算不分仓库,那就完了,几乎所有commit都得被改,那就改呗,至于别人,你管别人干嘛,让别人重新clone!

因为skia的repo也经常瞎搞,瞎搞的结果是SVN的UUID变,然后我每次都重新clone,nnd。

反正我的结论是:git之所以快,就是基于snapslot的方式所以才快,这是和其他版本控制工具(包括darcs)最不同的地方。git依然可以在content-addressable filesystem的基础上做基于patch的版本管理(这就是我说的图灵完全的意思!我不是说用C重新写一个,我是说Git的基本原理CAFS是对所有版本控制任务兼容的!!),这真的非常简单,只是所有的基本操作要重写一个而已,高层都可以不变。就是需要劳动力罢了。但是这不好。如果这样,git就完全无法保持现在这样的速度了!!

为了这样一个specfied的目的,牺牲git的最大优势(速度),我觉得不值。

如果要完成你的要求,也不是不行:1. notes做成独立分支,别的不管,新的master删掉notes目录,所有人从你的项目重新pull,然后git gc。2. notes抽出来做成独立repo,然后原项目删掉notes,commit,然后notes做submodule,别的不管。好处是别人不受影响,坏处是新人clone库的时候体积会很大,有很多垃圾。

至于到底是哪种都行,反正肯定比用darcs好。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP