31

我的问题是关于 Git 处理分支的方式:每当您从提交分支时,除非您通过合并强制它,否则该分支将永远不会收到来自父分支的更改。

但是在我们 Clearcase 或 Accurev 等其他系统中,您可以指定如何使用某种继承机制来填充分支:我的意思是,对于 Clearcase,使用 config_spec,您可以说“获取在分支 /main/issue001 上修改的所有文件和然后继续使用 /main 上的那些或使用这个特定的基线”。

在 Accurev 中,您也有一个类似的机制,它让流从上层分支(流如何调用它们)接收更改,而无需在分支上合并或创建新提交。

你在使用 Git 时不会错过这个吗?你能列举出这种继承是必须的场景吗?

谢谢

更新请阅读下面的 VonC 答案以真正关注我的问题。一旦我们同意“线性存储”和基于 DAG 的 SCM 具有不同的功能,我的问题是:哪些是现实生活场景(尤其是对于 OSS 以外的公司),线性可以做 DAG 不可能做的事情?他们值得吗?

4

9 回答 9

30

要理解为什么 Git 不提供您所说的某种“继承机制”(不涉及提交),您必须首先了解这些 SCM 的核心概念之一(例如 Git 与 ClearCase)

  • ClearCase 使用线性版本存储:元素(文件或目录)的每个版本都与同一元素的先前版本以直接线性关系链接。

  • Git 使用DAG -有向无环图:文件的每个“版本”实际上是树中全局更改集的一部分,而树本身就是提交的一部分。之前的版本必须在之前的提交中找到,可以通过单个有向无环图路径访问。

在线性系统中,配置规范可以指定几个规则来实现您看到的“继承”(对于给定的文件,首先选择某个版本,如果不存在,则选择另一个版本,如果不存在,则选择一个第三,以此类推)。

分支是线性历史中的一个分支,是给定选择规则的给定版本(在此之前的所有其他选择规则仍然适用,因此产生“继承”效果)

在 DAG 中,提交代表您将获得的所有“继承”;没有“累积”的版本选择。此图中只有一个路径可以选择您将在此确切点(提交)看到的所有文件。
分支只是此图中的一条新路径。

要在 Git 中应用其他版本,您必须:

  • 将其他一些提交合并到您的分支中(例如stsquad 的回答中提到的 git pull )或
  • 重新设置您的分支(正如Greg 提到的那样)

但由于 Git 是基于 DAG 的 SCM,它总是会导致新的提交。

您在 Git 中“失去”的是某种“组合”(当您选择具有不同连续选择规则的不同版本时),但这在D VCS中是不切实际的(如在“分布式”中):当您使用 Git 创建一个分支,您需要有一个明确定义的起点和内容,并且可以轻松复制到其他存储库。

在纯粹的中央 VCS 中,您可以使用您想要的任何规则来定义您的工作区(在 ClearCase 中,您的“视图”,快照或动态)。


unknown-google在评论中添加(以及上面的问题):

所以,一旦我们看到这两种模型可以实现不同的事情(线性与 DAG),我的问题是:哪些是现实生活场景(尤其是对于 OSS 以外的公司)线性可以做 DAG 不可能的事情?他们值得吗?

当谈到选择规则方面的“现实生活场景”时,您可以在线性模型中为同一组文件设置多个选择规则。

考虑这个“配置规范”(即 ClearCase 选择规则的“配置规范”):

element /aPath/... aLabel3 -mkbranch myNewBranch
element /aPath/... aLabel2 -mkbranch myNewBranch

它选择所有标记为“ aLabel2”的文件(并从那里分支),除了那些标记为“ aLabel3”的文件 - 并从那里分支 - (因为该规则在提到“ aLabel2”的那个之前)。

这值得么?

不。

实际上,出于简单的原因, ClearCase 的 UCM 风格(ClearCase 产品中包含的“统一配置管理”方法,代表从基本 ClearCase 使用中推导出的所有“最佳实践”)不允许这样做。一组文件称为“组件”,如果您想针对给定标签(称为“基线”)进行分支,则将按照以下配置规范进行翻译:

element /aPath/... .../myNewBranch
element /aPath/... aLabel3 -mkbranch myNewBranch
element /aPath/... /main/0 -mkbranch myNewBranch

您必须选择一个起点(此处为“ aLabel3”)并从那里开始。如果您还想要“ aLabel2”中的文件,则将所有“ ”文件合并aLabel2到“myNewBranch”中的文件。

这是您不必使用 DAG 进行的“简化”,其中图形的每个节点代表一个分支的唯一定义的“起点”,无论所涉及的文件集是什么。

合并和变基足以将该起点与给定文件集的其他版本结合起来,以实现所需的“组合”,同时将特定历史记录隔离在分支中。

总体目标是在“应用于连贯组件的连贯版本控制操作”中进行推理。“连贯”的文件集是处于明确定义的连贯状态的文件:

  • 如果已标记,则其所有文件都已标记
  • 如果分支,其所有文件将从相同的唯一起点分支

这在 DAG 系统中很容易做到;在线性系统中可能会更加困难(尤其是在“配置规范”可能很棘手的情况下使用“Base ClearCase”),但它是通过同一基于线性工具的 UCM 方法强制执行的。

不是通过“私有选择规则技巧”(使用 ClearCase,一些选择规则顺序)实现“组合”,而是仅通过 VCS 操作(变基或合并)实现它,这为每个人留下了清晰的跟踪(相反到开发人员私有的配置规范,或在一些但不是所有开发人员之间共享)。再次强调,与“动态灵活性”相反,它增强了一种连贯性您以后可能很难重现这种感觉。

这可以让你离开VCS(版本控制系统)的领域,进入SCM(软件配置管理)的领域,它主要关注的是“再现性”。而这(SCM 功能)可以通过基于线性或基于 DAG 的 VCS 来实现。

于 2009-04-18T21:11:13.487 回答
3

听起来您正在寻找的可能是git rebase. 重新定位分支在概念上将其与其原始分支点分离,并在其他点重新连接它。(实际上,变基是通过将分支的每个补丁依次应用到新分支点来实现的,创建一组新补丁。)在您的示例中,您可以将分支变基到上分支的当前尖端,这本质上将“继承”对另一个分支所做的所有更改。

于 2009-04-18T08:06:42.530 回答
3

我不完全清楚你的要求,但听起来 git 的跟踪语义就是你想要的。当您从 am origin 分支时,您可以执行以下操作:

git -t -b my_branch origin/master

然后未来的“git pull”会自动将 origin/master 合并到你的工作分支中。然后你可以使用“git cherry -v origin/master”来看看有什么区别。您可以在发布更改之前使用“git rebase”来清理历史记录,但一旦您的历史记录公开(即其他人正在关注该分支),则不应使用 rebase。

于 2009-04-18T10:31:49.093 回答
2

至于 accurev 使用的继承方案:当查看git-flow时,GIT 用户可能会“了解”全部内容(另请参见:http: //github.com/nvie/gitflowhttp://jeffkreftmeijer.com/ 2010/why-arent-you-using-git-flow/

这个 GIT 分支模型或多或少地(手动/在 git-flow 工具的帮助下)完成了 accurev 自动开箱即用的工作,并且具有强大的GUI 支持。

所以看起来GIT 可以做 accurev 所做的事情。因为我从来没有真正每天使用 git/git-flow,所以我不能真正说出它是如何工作的,但它看起来确实很有希望。(减去适当的 GUI 支持:-)

于 2010-09-10T13:58:42.157 回答
2

我会尽力回答你的问题。(这里不得不说,我没用过GIT,只是看了一下,所以如果我下面提到的有不对的地方还请指正)

“你能列举出这种继承是必须的场景吗?”

我不会说这是必须的,因为您可以使用您拥有的工具解决问题,并且可能是您环境的有效解决方案。我想这更多的是过程而不是工具本身。确保您的流程是连贯的,并且还允许您及时返回以重现任何中间步骤/状态是目标,而且该工具让您可以尽可能轻松地运行您的流程和 SCMP

我可以看到拥有这种“继承”行为并使用配置规范的强大功能很方便的一种情况是,当您希望将一组更改“隔离”映射到任务(devtask、CR、SR 或任何定义的变更集的目的/范围)

使用此组合可以让您的开发分支干净,并且仍然使用其余代码的不同组合(使用组合),并且在任务的整个生命周期中仍然只有与分支中隔离的任务相关的内容,直到整合阶段。

作为纯粹主义者必须提交/合并/变基只是为了有一个“定义的起点”,我想它会“污染”你的分支,你最终会在你的分支/更改集中进行更改+其他更改。

这种隔离何时/何地有用?以下几点可能仅在公司追求 CMM 和某些 ISO 认证的背景下有意义,并且可能对其他类型的公司或 OSS 不感兴趣

  • 由于非常挑剔,您可能希望准确计算对应于单个开发人员的更改集的代码行数(添加/修改/删除),稍后用作代码和工作量估算的一个输入。

  • 可以更轻松地在不同阶段查看代码,只需将您的代码放在一个分支中(不与其他更改粘连)

在有多个团队和 +500 名开发人员同时在同一个基本代码上同时工作的大型项目中,(其中图形化的单个元素版本树看起来像一个杂乱的错综复杂的网络,有多个负载线,每个大客户一个,或者每个技术一个)大型配置使用深度组合的规范使这么多人无缝地工作,以使相同的产品/系统(基本代码)适应不同的目的。使用此配置规范,动态地为每个团队或子团队提供不同的视图,以了解他们需要什么以及他们需要从哪里分支,(在几种情况下级联),而无需创建中间集成分支,或不断合并和重新定位所有您需要开始的位。来自相同任务/目的的代码是不同标签的分支,但很有意义。(您可以在此处争论“已知基线”作为 SCM 的原则,但书面 SCM 计划中考虑的简单标签完成了工作)必须有可能用 GIT 解决这个问题(我猜是以非动态方式)但我发现如果没有这种“继承”行为,真的很难想象。我猜 VonC 提到的“如果分支,它的所有文件都将从同一个唯一起点分支”在这里被破坏了,但除了它在 SCMP 上有很好的记录之外,我记得这样做有充分的商业理由。

是的,构建我上面提到的这些配置规范并不是免费的,一开始有 4-5 个高薪人员在 SCM 后面,但后来被自动脚本减少了,这些脚本询问你在标签/分支/功能方面想要什么,并将为你写CS。

这里的可重复性是通过将配置规范与任务一起保存在 devTask 系统中来实现的,因此上游的每个任务都映射到需求,下游映射到配置规范,一组更改(代码文件、设计文档、测试文档ETC)

因此,到这里为止,一个结论可能是,只有当您的项目足够大/足够复杂(并且您可以在项目的整个生命周期中负担得起 SC 经理 :))时,您才会开始考虑是否需要“继承”行为或真正通用的工具,否则您将直接使用免费的工具,并且已经处理了 SCM 的连贯性……但是 SCM 工具上可能还有其他因素可能使您坚持使用一个或另一个.. . 继续阅读..

一些旁注,可能超出主题,但我想在某些情况下需要考虑我的情况。

我必须在这里补充一点,我们使用的是“good-ol CC”而不是 UCM。完全同意 VonC 关于允许“引导”灵活性以实现更连贯配置的良好方法。好消息是 CC 非常灵活,您可以(并非不费力气)找到一种使事情连贯的好方法,而在其他 SCM 中您可能会免费获得它。但是例如在这里(以及我与 CC 合作过的其他地方)对于 C/C++ 项目,我们无法承受没有winkin的代价功能(重用派生对象),减少了几倍的编译时间。可以说,拥有更好的设计,更解耦的代码,优化 Makefile 可以减少编译整个东西的需要,但有些情况下你需要一天编译多次,共享 DO 可以节省大量的时间/金钱。我现在在哪里,我们尝试尽可能多地使用免费工具,我认为如果我们能找到更便宜或免费的工具来实现winkin功能,我们将摆脱 CC。

我将用 Paul 提到的一些东西来结束,不同的工具对于不同的目的比其他的更好,但我会补充一点,你可以通过一个连贯的过程来摆脱工具的一些限制,而不会影响可重复性,SCM 中的关键点最后我猜这个答案值得吗?取决于您的“问题”、您正在运行的 SDLC、您的 SCM 进程,以及是否有任何额外的功能(如 winkin)可能在您的环境中有用。

我的 2 美分

于 2010-10-18T23:18:52.370 回答
2

您是否注意到您也可以使用 GIT 检查特定的文件版本?

只需使用这个:

git checkout [< tree-ish >] [--] < paths >

像每个配置规范一样,任何现有版本的文件(路径)都可以加载到工作树中。引用 git-checkout 文档:

以下序列检查出 master 分支,将 Makefile 恢复为两个修订版,hello.c错误地删除,然后从索引中取回:

$ git checkout master             
$ git checkout master~2 Makefile             
$ rm -f hello.c            
$ git checkout hello.c            
于 2010-11-11T08:29:09.993 回答
2

抛开理论不谈,从我多年来在商业生产环境中使用 AccuRev 的角度来看,这里有一种明显的实际看法:只要子流与祖先没有太大分歧,继承模型就可以很好地工作。仍在开发中。当继承流太不同时,它就会崩溃。

继承(较早版本的子版本)允许祖先流中的更改在子流中处于活动状态,而无需任何人做任何事情(除非需要合并,在这种情况下它显示为深度重叠,很高兴能够看到)。

这听起来很棒,在实践中,当所有涉及的流都相对相似时。我们将该模型用于给定生产版本以下的修补程序和服务包级别流。(这对我们来说实际上比这要复杂一些,但这是一般的想法。)

生产版本是并行的,没有继承性,在它们下面都有那些修补程序和服务包子项。启动一个新版本意味着创建一个新的版本级别流,并手动将前一个版本的最新维护流中的所有内容推送到其中。之后,必须手动将适用于较晚版本的早期版本的更改推送到每个版本中,这需要更多的工作,但允许更大的控制。

我们最初在所有版本中都使用继承模型,后来的版本是早期版本的子版本。这在一段时间内运作良好,但随着时间的推移变得难以管理。不同版本之间的主要架构差异使得不可避免地继承更改成为一个坏主意。是的,您可以在两者之间放置一个快照来阻止继承,但是所有更改都必须手动推送,并且父快照子和并行非继承流之间唯一真正的区别是整个图形流视图不断向下推送在右边,这是一个 PITA。

AccuRev 的一个非常好的事情是您始终可以选择。这不是 SCM 程序架构的固有约束。

于 2012-09-08T13:35:30.330 回答
1

没有 MultiSite 的 ClearCase 是单个存储库,但 Git 是分布式的。ClearCase 在文件级别提交,但 Git 在存储库级别提交。(最后一个区别意味着原始问题是基于误解,正如此处其他帖子中所指出的那样。)

如果这些是我们正在谈论的差异,那么我认为“线性”与“DAG”是区分这些 SCM 系统的一种令人困惑的方式。在 ClearCase 中,文件的所有版本都称为文件的版本“树”,但实际上它是一个有向无环图!与 Git 的真正区别在于每个文件都存在 ClearCase 的 DAG。所以我认为将 ClearCase 称为非 DAG 而将 Git 称为 DAG 是一种误导。

(顺便说一句,ClearCase 以与其文件类似的方式对其目录进行版本化 - 但这是另一回事。)

于 2009-11-30T14:26:44.160 回答
0

我不确定您是否在问什么,但您正在证明 Accurev 流是与 Git(或 SVN)分支不同的工具。(我不知道 Clearcase。)

例如,正如您所说,使用 Accurev,您被迫使用某些工作流程,这为您提供了 Git 不支持的可审计的更改历史记录。Accurev 的继承使某些工作流程更高效,而另一些则不可能。

使用 Git,您可以在本地存储库或功能分支中隔离探索性编码,而 Accurev 不能很好地支持。

不同的工具适用于不同的目的;询问每个人的优点是很有用的

于 2009-04-18T14:33:12.443 回答