我的办公室希望无限分支机构合并为政策;我们还有什么其他选择?


119

我的办公室正在尝试弄清楚我们如何处理分支拆分和合并,但是我们遇到了一个大问题。

我们的问题是长期的分支机构-在这种情况下,您需要几个人在与分支机构分离的分支机构中工作,我们会发展几个月,而当我们达到一个里程碑时,我们就会将两者同步。

现在,恕我直言,处理此问题的自然方法是将侧枝压缩为单个提交。master不断进步;应当-我们并没有追溯数月的并行开发master历史。而且,如果有人需要更好的分辨率来解决分支机构的历史,那么当然,它仍然存在-它不在master,而是在分支机构中。

问题出在这里:我只使用命令行工作,而我团队的其他成员则使用GUIS。而且我发现GU​​IS没有合理的选项来显示其他分支的历史记录。因此,如果您达成一个壁球承诺,说“此开发已从分支被压缩XYZ”,那么查看其中的内容将是巨大的痛苦XYZ

在SourceTree上,据我所知,这非常令人头疼:如果您正在上master,并且想要查看的历史记录master+devFeature,则需要签master+devFeature出(触摸每个不同的文件),否则滚动浏览一个日志,该日志并行显示您存储库的所有分支,直到找到正确的位置。祝您好运。

我的队友很正确地不想拥有如此难以接近的发展历史。因此,他们希望合并这些大型的,长期的开发分支,并始终使用合并提交。他们不需要任何无法从master分支立即访问的历史记录。

讨厌那个主意;这意味着平行发展历史的无尽,不可逾越的纠结。但是我没有看到我们有什么选择。而且我很困惑。这似乎阻碍了我对良好分支机构管理所了解的大多数知识,如果找不到解决方案,这将使我不断感到沮丧。

除了不断地将分支机构与合并提交合并到主分支之外,我们这里还有其他选择吗?还是有一个原因导致不断使用merge-commits并不像我担心的那样糟糕?


84
将合并记录为合并真的那么糟糕吗?我可以看到,挤入线性历史具有其优势,但令我惊讶的是,不这样做会违反“您对良好分支机构管理所知的大多数知识”。您到底担心什么问题?
IMSoP '16

65
OK,但在我的脑海里长时间运行的分支也正是你想要一些知名度,使指责和平分并不只是落在“的方式进行了更改为2016年大重写的一部分。” 我没有足够的信心来回答这个问题,但是我的直觉是应该压缩功能分支中的任务,以便您可以从主要历史记录中访问该项目的简短历史记录,而不必签出一个孤立的分支。
IMSoP '16

5
话虽这么说,所以问题很可能简化为“我正在尝试以比队友更高的水平使用git;如何防止他们为我搞乱。” 公平地说,我只是没想到git log master+bugfixDec10th会成为突破点:-/
站在

26
“长期分支机构-在这种情况下,有几个人在与分支机构分离的分支机构工作,我们发展了几个月,当达到一个里程碑时,我们将两者同步。” 您是否不定期从主服务器拉到分支机构?在很多情况下,这样做(很少)致力于掌握,往往会使生活变得更简单。
TafT '16

4
merge --no-ff你想要的吗?在master本身你有一个承诺,描述在分支发生了什么变化,但所有提交的依然存在并有向父头。
CAD97

Answers:


244

即使我在命令行上使用Git,也必须与您的同事达成共识。将大型更改压缩为单个提交是不明智的。您正在以这种方式失去历史,而不仅仅是使其不那么可见。

源代码控制的重点是跟踪所有更改的历史记录。什么时候改变了为什么?为此,每个提交都包含指向父提交,diff和元数据(如提交消息)的指针。每次提交都描述源代码的状态以及导致该状态的所有更改的完整历史记录。垃圾收集器可能会删除无法到达的提交。

变基,采摘或挤压等操作会删除或重写历史记录。特别是,生成的提交不再引用原始提交。考虑一下:

  • 您压缩了一些提交,并在提交消息中注意到,压缩的历史记录在原始提交abcd123中可用。
  • 删除[1]所有包含abcd123的分支或标记,因为它们已合并。
  • 您让垃圾收集器运行。

[1]:一些Git服务器允许保护分支以防意外删除,但我怀疑您想保留所有功能分支永久存在。

现在您不再可以查找该提交-它根本不存在。

在提交消息中引用分支名称甚至更糟,因为分支名称在存储库中是本地的。什么是master+devFeature在您的本地结账可能会doodlediduh在我的。分支只是移动指向某个提交对象的标签。

在所有历史记录重写技术中,变基是最有益的,因为它会将完整的提交与所有历史记录一起复制,并仅替换父提交。

这在master历史上包括被合并到这是一件好事,所有分支的完整历史,因为这代表了现实。[2]如果有并行开发,则应该在日志中可见。

[2]:由于这个原因,我也更喜欢显式合并提交,而不是线性化但最终由于伪造而产生的虚假历史记录。

在命令行上,git log尝试简化显示的历史记录,并使所有显示的提交保持相关。您可以调整历史记录简化以适合您的需求。您可能很想编写自己的git日志工具来遍历提交图,但是通常无法回答“此提交最初是在此分支上提交的吗?”。合并提交的第一个父级是上一个HEAD,即您要合并到的分支中的提交。但这假设您没有进行从master到功能分支的反向合并,然后是将master快进到合并的反向合并。

我遇到的长期分支的最佳解决方案是防止几个月后合并的分支。当更改最近且很小时,合并是最容易的。理想情况下,您每周至少合并一次。持续集成(例如在极限编程中,而不是在“让我们设置Jenkins服务器”中),甚至建议每天进行多次合并,即不维护单独的功能分支,而是作为一个团队共享一个开发分支。在对功能进行质量检查之前进行合并要求将功能隐藏在功能标志的后面。

作为回报,频繁的集成可以更早地发现潜在的问题,并有助于保持一致的体系结构:可能会产生深远的变化,因为这些变化会很快包含在所有分支机构中。如果更改中断了某些代码,则只会中断几天的工作,而不是几个月。

当有成千上万的代码行和成百上千的活跃开发人员时,历史重写对于真正的大型项目可能是有意义的。令人怀疑的是,为什么这么大的项目必须是单个git repo而不是分成单独的库,但是如果中央repo只包含各个组件的“发行版”,那么在这种规模上会更方便。例如,Linux内核使用压榨来保持主要历史记录的可管理性。一些开源项目要求通过电子邮件发送补丁,而不是git级合并。


43
@Standback我不在乎开发人员在本地做什么……使用“ wip”提交,每个固定错字的额外提交……。很好,最好经常提交。理想情况下,开发人员会在推送之前清除这些提交,例如合并所有仅能修复某些错字的提交。如果提交执行不同的操作,则保持提交分开是一个好主意,例如,一个提交用于实际功能和相关测试,另一个提交用于无关的错别字。但是一旦提交被推送,重写或删除历史记录就比它值得的麻烦更多,至少以我的经验而言。
天宫院

11
“通常不可能回答“此提交最初是在此分支上还是那个分支上提交的?”。我真的很想问我的版本控制系统“日期y在分支x中是什么”
Peter Green

80
没有什么比使用尝试识别代码中的错误原因更令人沮丧的了git bisect,只是让它从侧分支到达10,000行uber-commit。
tpg2114 '16

2
@Standback恕我直言,提交越小,可读性和友好性就越高。乍一看,触及多于几个点的提交是不可能的,因此您只需以表面价值作为描述即可。那是描述的可读性(例如“实现的功能X”),而不是提交(代码)本身。我希望有1000个单字母提交,例如“将http更改为https”:)
Agent_L 2016年

2
cherry-pick不会重写或删除历史记录。它只是创建新的提交来复制现有提交的更改
Sarge Borsch

111

我喜欢Amon的回答,但我觉得其中一小部分需要更多强调: 您可以在查看日志以满足您的需求时轻松地简化历史记录,但是其他人不能在查看日志以满足他们的需求时添加历史记录。 这就是为什么最好保留历史记录。

这是我们的一个存储库中的一个示例。我们使用请求请求模型,因此每个功能看起来都像是您历史悠久的分支机构,即使它们通常只运行一周或更短的时间。个别开发人员有时会选择合并前的历史记录,但是我们经常对功能进行配对,因此相对来说并不常见。这是gitkgit附带的gui中最重要的几个提交:

标准gitk视图

是的,有点纠结,但我们也喜欢它,因为我们可以准确地看到谁在什么时候发生了什么变化。它准确地反映了我们的发展历史。如果要查看更高级别的视图,一次合并一个拉取请求,可以查看以下视图,该视图等效于git log --first-parent命令:

使用--first-parent的gitk视图

git log还有许多其他选项,旨在为您提供所需的精确视图。 gitk可以采用任何任意git log参数来构建图形视图。我确定其他GUI也具有类似的功能。阅读文档并学习正确使用它,而不是git log在合并时对每个人强制使用您的首选视图。


24
我不熟悉此选项!这对我来说是一个巨大的帮助,听起来像是学习更多的日志选项会让我更轻松地生活在这里。
站在

25
+1表示“您可以在查看日志时轻松简化历史记录以满足您的需求,但是其他人在查看日志时不能添加历史记录以满足他们的需求。” 重写历史总是有问题的。如果您认为在提交时进行记录很重要,那么它就很重要。即使您发现它是错误的,也可能稍后再做,这都是历史的一部分。仅当您可以指责这一行是在以后的重写中遗留下来的时候,才有一些错误才有意义。当错误与其他史诗相提并论时,您将无法回顾为什么事情最终会变成现实。
TafT '16

@Standback Related,帮助我保持这种结构的一件事,是在使用merge --no-ff-不要使用快进合并,而总是创建合并提交,以便可以--first-parent使用
Izkata 2016年

34

我们的问题是长期的分支机构-在这种情况下,您需要几个人在与分支机构分离的分支机构中工作,我们会发展几个月,而当我们达到一个里程碑时,我们就会将两者同步。

我的第一个想法是-除非绝对必要,否则不要这样做。您的合并有时必须具有挑战性。保持分支独立并且寿命短。这表明您需要将故事分解为较小的实现块。

如果必须执行此操作,则可以使用--no-ff选项将其合并到git中,以使历史记录在各自的分支上保持不同。提交仍将出现在合并的历史记录中,但也可以在功能分支上单独看到,以便至少可以确定它们属于哪条开发线。

我不得不承认,当我第一次开始使用git时,发现合并后分支提交出现在与主分支相同的历史记录中有点奇怪。这有点令人不安,因为这些提交似乎不属于该历史记录。但是在实践中,如果人们认为集成分支就是那样,那根本不是一件痛苦的事情,它的全部目的是合并功能分支。在我们的团队中,我们不压榨,并且我们经常进行合并提交。我们一直使用--no-ff来确保在我们想要调查任何功能时,可以轻松查看其确切历史。


3
我完全同意;每个人都更喜欢紧贴主人。但这是一个不同的问题,在我自己的谦卑影响下,这个问题要大得多,而远没有那么多:-P
站在2016年

2
+1git merge --no-ff
0xcaff,2016年

1
也为+1 git merge --no-ff。如果您使用gitflow,它将成为默认值。
大卫·哈门

10

让我直接明确地回答您的观点:

我们的问题是长期的分支机构-在这种情况下,您需要几个人在与分支机构分离的分支机构中工作,我们会发展几个月,而当我们达到一个里程碑时,我们就会将两者同步。

通常,您几个月都不想让您的分支不同步。

根据您的工作流程,功能分支已经分支了。master为了简单起见,我们称之为它。现在,只要您致力于掌握,就可以并且应该git checkout long_running_feature ; git rebase master。这意味着,根据设计,您的分支始终保持同步。

git rebase这也是正确的做法。这不是hack,也不是奇怪或危险的东西,而是完全自然的。您丢失了一点信息,这就是功能分支的“生日”,仅此而已。如果有人认为这很重要,可以通过将其保存在其他地方(在票务系统中,或者如果需要的话在git tag...中)来提供。

现在,恕我直言,处理此问题的自然方法是将侧枝压缩为单个提交。

不,您绝对不想要那样,您想要一个合并提交。合并提交也是“单个提交”。它不以某种方式不将所有单个分支提交“插入” master。这是一次带有两个父级的提交- master合并时的头和分支头。

当然,请务必指定--no-ff选项;--no-ff在您的情况下,严格禁止不合并。不幸的是,--no-ff这不是默认值。但我相信您可以设置一个选项。看看git help merge有什么--no-ff作用(简而言之:它激活了我在上一段中描述的行为),这一点至关重要。

我们不会将数月的并行开发追溯到大师的历史中。

绝对不是-您永远不会将某些内容“转储到”某个分支的历史记录中,尤其是不要使用合并提交。

而且,如果有人需要更好的分辨率来解决分支机构的历史,那么,当然,所有这些仍然存在-它不是大师,而是分支机构。

有了合并提交,它仍然存在。不在母带中,而是在分支中,作为合并的父项之一清晰可见,并应保持永恒,如其应有的那样。

看看我做了什么?你描述你的壁球所有的事情都承诺在那里与合并--no-ff提交。

问题出在这里:我只使用命令行工作,而我团队的其他成员则使用GUIS。

(旁注:我几乎还专门使用命令行(嗯,这是个谎言,我通常使用emacs magit,但这是另一个故事-如果我不方便使用单独的emacs设置,我更喜欢使用命令线也一样)。但是请不要自己一个忙,至少尝试git gui一次。它是如此采摘线路,帅哥等添加更加高效/撤销补充道。)

而且我发现GU​​IS没有合理的选项来显示其他分支的历史记录。

那是因为您试图做的完全违背了精神gitgit从核心建立在“有向无环图”上,这意味着很多信息都在提交的父子关系中。而且,对于合并,这意味着真正的合并是由两个父母和一个孩子提交的。只要使用no-ff合并提交,同事的GUI就可以了。

因此,如果您达成壁球承诺,说“此开发已从XYZ分支压缩”,那么去查看XYZ中的内容将是巨大的痛苦。

是的,但这不是GUI的问题,而是南瓜提交的问题。使用壁球意味着您将要素分支头悬空,并在其中创建了一个全新的提交master。这从两个层次上破坏了结构,造成了很大的混乱。

因此,他们希望合并这些大型的,长期的开发分支,并始终使用合并提交。

他们是绝对正确的。但他们不是“合并 ”,他们只是合并。合并是一个真正平衡的事情,它没有将“合并”为另一侧的首选面(除了微小的视觉差异(如分支被交换等)外,其他git checkout A ; git merge B都完全相同)。git checkout B ; git merge Agit log

他们不需要任何无法从master分支立即访问的历史记录。

这是完全正确的。在没有未合并功能的时候,您将拥有一个master具有丰富历史记录的分支,其中囊括了曾经存在的所有功能提交行,git init从一开始就回溯到提交(请注意,我特别避免使用术语“在该段的后半部分“分支”,因为当时的历史不再是“分支”,尽管提交图会非常分支。

我讨厌那个主意;

然后您会感到有些痛苦,因为您正在使用所使用的工具。这种git方法非常优雅和强大,特别是在分支/合并区域。如果您做对了(如上文所提到,尤其是使用--no-ff),则它比其他方法(例如,具有分支的并行目录结构的颠覆性混乱)优越得多。

这意味着平行发展历史的无尽,不可逾越的纠结。

无尽,平行-是的。

无法导航,纠结-不。

但是我没有看到我们有什么选择。

为什么不git每天都像发明家,您的同事和世界各地的发明家那样工作?

除了不断地将分支机构与合并提交合并到主分支之外,我们这里还有其他选择吗?还是有一个原因导致不断使用merge-commits并不像我担心的那样糟糕?

没有其他选择;还不错


10

压榨长期的分支机构会使您丢失很多信息。

我要做的是在将分支机构合并为master之前,尝试将master设置为长期分支机构。这样,您可以将每次提交都保留在master中,同时使提交历史记录变得线性且更易于理解。

如果我不能在每次提交时轻松地做到这一点,我将其设为非线性的,以保持开发环境的清晰。我认为,如果在将母版改编为支票的过程中合并时出现问题,则意味着非线性具有现实意义。这意味着如果我需要深入了解历史,将会更容易理解发生了什么。我还获得了不必重新设置基准的直接好处。


:+提及rebase。不管它是否是这里最好的方法(我个人并没有很好的经验,尽管我没有使用过很多),但这绝对是与OP真正想要的最接近的东西-特别是,在无序的历史记录转储和完全隐藏该历史记录之间进行折衷。
凯尔·斯特兰德

1

就个人而言,我更喜欢分叉进行开发,然后提取请求以合并到主存储库中。

这意味着,如果我想将更改基于基础,或压榨一些WIP提交,则完全可以做到这一点。或者,我也可以要求将我的全部历史记录也合并。

喜欢做的是做我发展的一个分支,但经常对重订主的/ dev。这样,我得到的最新师父修改,而无需一堆合并的提交到我的分支,或有应付合并冲突的整个负载时,它的时间合并掌握。

要明确回答您的问题:

除了不断地将分支机构与合并提交合并到主分支之外,我们这里还有其他选择吗?

是的-您可以在每个分支上合并一次(当功能或修订“完成”时),或者如果您不希望在历史记录中进行合并提交,则可以在做完最终基准后简单地对master进行快速向前合并。


3
抱歉-我做同样的事,但是我看不出这怎么回答这个问题:-/
Standback

2
为您陈述的问题添加了明确的答案。
韦恩·沃纳

因此,这对于我自己的提交很好,但是我(和我的团队)将处理每个人的工作。保持自己的历史记录很容易;在混乱的开发历史中工作是这里的问题。

您的意思是您想要求其他开发人员来...什么?不重新设置基础,只是合并而已?还是您只是发布此问题来困扰您的同事缺乏git纪律?
韦恩·沃纳

1
如果多个开发人员正在该功能分支上,则定期重新定基是没有用的。
圣保罗Ebermann

-1

修订控制是垃圾回收。

问题是功能分支上的正在进行中的工作可能包含很多“让我们尝试一下……不行,不行,让我们替换为那个”以及除最后一个“ that”之外的所有提交都结束浪费了历史。

最终,应保留历史记录(将来可能会使用其中的某些内容),但仅应合并“干净副本”。

使用Git,可以通过首先分支功能分支(保留所有历史记录),然后(交互地)从master重新建立功能分支的分支,然后合并重新建立的分支来完成此操作。


1
只是为了澄清一下:您的意思是重写功能分支(的副本)上的历史记录,因此它具有简短的历史记录-消除了所有最初开发的来龙去脉。然后,将重写的分支合并到master中变得更加简单和简洁。我对你的理解正确吗?
站在

1
是。而当您合并到master时,这将是一个快速的前进(假设您在合并之前最近重新调整了基础)。
DepressedDaniel

但是如何保留这个历史呢?您是否让原始要素分支随处可见?
圣保罗Ebermann

@PaŭloEbermannBingo。
DepressedDaniel

嗯,正如其他人在评论中所说:分支只是指向中的历史记录图的指针git,它们是本地的。什么被命名为foo一个回购可能被命名为bar不同的。而且它们只是暂时的:您不想让成千上万的功能分支混乱,以免丢失历史记录,从而使您的仓库变得混乱。并且您需要保留这些分支以便保留历史记录作为参考,因为git最终将删除该分支不再可访问的任何提交。
cmaster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.