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

我们的问题是长期的分支机构-您所拥有的分支机构几个人在与分支机构分离的分支机构工作,我们发展了几个月,当我们达到一个里程碑时,我们将两者同步了。

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

这是问题所在:我专门使用命令行,但我们团队的其余成员使用GUIS。而且我发现GU​​IS没有合理的选项来显示其他分支的历史记录。因此,如果您达成一个壁球提交,说“此开发已从分支XYZ压缩”,那么去看看XYZ中的内容将非常痛苦。这是一个巨大的头痛:如果您使用的是master,并且想查看master+devFeature的历史记录,则需要签出master+devFeature(触摸每个不同的文件),或者滚动查看显示所有存储库分支的日志平行,直到找到正确的位置。祝您好运。
很正确,我的队友们不想拥有如此难以接近的发展历史。因此,他们希望合并这些大型的,长期的开发分支,并始终使用合并提交。他们不希望从master分支无法立即访问的任何历史记录。

我讨厌那个主意;这意味着平行发展历史的无尽,不可逾越的纠结。但是我没有看到我们有什么选择。而且我很困惑。这似乎挡住了我对良好分支机构管理所了解的大部分知识,如果找不到解决方案,这将使我不断感到沮丧。分支合并到合并提交大师?还是有一个原因导致不断使用merge-commits并不像我担心的那样糟糕?

评论

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

好的,但是在我看来,您确实希望在一个长期运行的分支中拥有一些知名度,因此,责任与二等分不仅仅着眼于“这一变化是在《 2016年大重写》中引入的”。我没有足够的信心来回答这个问题,但是我的直觉是应该压缩功能分支中的任务,以便您可以从主要历史记录中访问该项目的简短历史记录,而不必检查一个孤立的分支。

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

“长期分支机构-在这种情况下,您需要几个人在与分支机构分离的分支机构中工作,我们会发展几个月,而当我们达到一个里程碑时,我们就会将两者同步。”您是否不定期从母版拉到分支机构?在许多情况下,每(很少)位专家都会这样做,这会使生活变得更简单。

您要合并--no-ff吗?在master本身上,有一个提交描述了分支中所做的更改,但是所有提交仍然存在并且以HEAD为父。

#1 楼

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

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

诸如变基,摘樱桃或压榨之类的操作会删除或重写历史记录。特别是,生成的提交不再引用原始提交。请考虑以下问题:


您压缩了一些提交,并在提交消息中注意到,被压缩的历史记录在原始提交abcd123中可用。包含abcd123,因为它们已合并。
您让垃圾收集器运行。

[1]:某些Git服务器允许保护分支以防意外删除,但我怀疑您是否希望保留所有您的功能分支将永远存在。

现在您不能再查找该提交–它根本不存在。

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

在所有历史记录重写技术中,重定基数是最有益的,因为它会复制其全部历史记录中的完整提交,并替换父提交。 br />
master的历史记录包含合并到其中的所有分支的完整历史记录是一件好事,因为它代表了现实。[2]如果有并行开发,则应该在日志中可见。

[2]:因此,相对于线性化但由于重定基准最终导致的伪造历史记录,我也更倾向于显式合并提交。

在命令行上,git log试图简化显示的历史记录并保持所有显示提交相关。您可以调整历史记录简化以适合您的需求。您可能很想编写自己的git log工具来遍历提交图,但是通常无法回答“此提交最初是在此分支上提交的吗?”。合并提交的第一个父级是先前的HEAD,即您要合并到的分支中的提交。但这是假设您没有执行从master到功能分支的反向合并,然后是快速转发的master到合并的事情。防止几个月后才合并的分支。当更改最近且很小时,合并是最容易的。理想情况下,您每周至少合并一次。持续集成(例如在极限编程中,而不是在“让我们设置Jenkins服务器”中),甚至建议每天进行多次合并,即不维护单独的功能分支,而是作为一个团队共享一个开发分支。在对功能进行质量检查之前进行合并要求将功能隐藏在功能标志的后面。

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

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

评论


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

–阿蒙
16 Dec 19'在17:09

“通常无法回答“此提交最初是在此分支或那个分支上提交的吗?”。”我看到一个事实,即“分支”只是指向没有实际历史的提交的指针,这是git中最大的设计缺陷之一。我真的很想问我的版本控制系统“日期y在分支x中是什么”。

– Peter Green
16/12/19在18:40

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

–tpg2114
16 Dec 20'在1:14

@Standback恕我直言,提交越小,可读性和友好性就越高。乍看之下,涉及多个点的提交是不可能把握的,因此您只需要描述其面值即可。那是描述的可读性(例如“已实现的功能X”),而不是提交(代码)本身。我宁愿有1000个单字母提交,例如“将http更改为https”:)

–Agent_L
16 Dec 20'在11:23

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

–显示名称
16年12月21日在20:51

#2 楼

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

这是我们一个存储库中的一个示例。我们使用请求请求模型,因此每个功能看起来都像您历史上长期运行的分支,即使它们通常只运行一周或更短的时间。个别开发人员有时会选择合并前的历史记录,但是我们经常对功能进行配对,因此相对来说并不常见。这是与git捆绑在一起的gui gitk中的前几个提交:是的,有点纠结,但是我们也喜欢它,因为我们可以看到确切地说,谁在什么时候发生了什么变化。它准确地反映了我们的发展历史。如果要查看更高级别的视图,一次合并一个拉取请求,可以查看以下视图,它等效于git log --first-parent命令:



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

评论


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

–备用
16/12/19在17:42

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

– TafT
16年12月20日在8:30

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

–伊兹卡塔
16 Dec 24'在23:29

#3 楼


我们的问题是长期的分支机构–您所拥有的分支机构
几个人在与分支机构分离的分支机构工作,我们发展了几个月,等到我们达到一个里程碑,我们将两者同步。


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

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

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

评论


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

–备用
16年12月19日在19:39

+1为git merge --no-ff

– 0xcaff
16年12月22日在7:23

对于git merge --no-ff也为+1。如果使用gitflow,它将成为默认值。

–David Hammen
16/12/22在16:36

#4 楼

让我直接和清楚地回答您的观点:


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


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

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

git rebase也是正确的选择。这不是骇客,怪异或危险的事物,而是完全自然的。您丢失了一点信息,这就是功能分支的“生日”,仅此而已。如果有人认为这很重要,可以通过将其保存在其他地方(在票务系统中,或者如果需要的话,在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没有合理的选项来显示其他分支的历史记录。


那是因为您尝试做的事情完全违背了git的精神。 git从核心建立在“有向无环图”上,这意味着很多信息都在提交的父子关系中。而且,对于合并而言,这意味着要由两个父母和一个孩子进行真正的合并。只要您使用no-ff合并提交,您同事的GUI就会很好。巨大的痛苦去看看XYZ中有什么。


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


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


它们是绝对正确的。但是它们不是“合并”的,它们只是合并的。合并是真正平衡的事情,它没有将“合并”到另一方的首选方面(git checkout A ; git merge Bgit checkout B ; git merge A完全相同,除了细微的视觉差异,例如在git log中交换分支等)。


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


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


;


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


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


无尽,平行-是。

无法导航,缠结-否。


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


为什么不像git的发明者,您的同事和世界其他地方一样每天工作呢?


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


没有其他选择;这是为什么?还不错。

#5 楼

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

我要做的是在将分支机构合并为master之前,先将master改成长期分支机构。这样,您可以将每个提交保留在master中,同时使提交历史成为线性且更易于理解。

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

评论


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

–凯尔·斯特兰德(Kyle Strand)
16 Dec 22 '23:00

#6 楼

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

这意味着,如果我想基于我的更改重新构建主数据库,或者压缩一些WIP提交,我完全可以做到。或者我也可以要求将我的整个历史也合并。

我喜欢做的事情是在分支上进行开发,但经常基于master / dev进行基础开发。这样,我就可以从master获得最新的更改,而无需在分支中进行大量合并提交,也不必在合并到master时处理整个合并冲突。

要显式回答您的问题:


除了将分支分支不断合并到带有合并提交的master之外,我们还有其他选择吗?


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

评论


抱歉-我做同样的事情,但是我看不出这如何回答问题:-/

–备用
16 Dec 19 '21:24



为您陈述的问题添加了明确的答案。

–Wayne Werner
16 Dec 19 '21:29

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

–备用
16 Dec 19 '21:50

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

–Wayne Werner
16 Dec 19 '21:56

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

–PaŭloEbermann
16 Dec 20'在0:30

#7 楼

修订控制是垃圾回收。

问题是功能分支上的正在进行中的工作可能包含很多“让我们尝试一下……不行,让我们用“”代替它,除最后一个“那个”之外的所有提交最终都将无用地污染历史。

最终,应保留历史记录(其中一些可能在历史记录中有用)

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

评论


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

–备用
16/12/19在22:37

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

–沮丧Daniel
16 Dec 19 '23:04

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

–PaŭloEbermann
16 Dec 20'0:32

@PaŭloEbermannBingo。

–沮丧Daniel
16 Dec 20'0:39

好吧,正如其他人在评论中所说:分支只是git中历史记录图的指针,它们是本地的。在一个仓库中被称为foo的东西可能在另一个仓库中被命名为bar。它们只是暂时的:您不想让成千上万的功能分支杂乱无章,而这些功能分支需要保持活动状态以避免丢失历史记录。而且,您需要保留这些分支以便保留历史记录的引用,因为git最终将删除分支不再可以访问的所有提交。

–cmaster-恢复莫妮卡
16 Dec 25 '21:48