- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想知道所有工具如何知道合并了哪些分支/提交,直到在提交中找到“合并”标头。
我的问题是:为什么git merge --squash
不添加标题,而git merge
却添加标题?
换句话说:为什么在与git merge
合并时没有合并边缘,为什么在与git merge --squash
合并时看到合并边缘?
谢谢。
一些修改后的信息:
“合并标头”是指合并后git log
中的第二行:
commit 7777777
Merge: 0123456 9876543
Author: Some Body <...>
Date: Fri ....
git merge --squash
不会产生该行,而我的假设是git gui工具读取该标头以能够绘制该“合并边缘”,请参见下图。
最佳答案
TL; DR:git merge --squash
合并(动词),但不合并(名词)。
您提到的“合并标头”不在该表单的提交中。相反,这只是git log
在遇到合并提交时打印的内容。为了真正巩固这个想法,让我们看一下合并的含义(“合并”作为动词的含义)以及合并是什么。更正确地,让我们看一下什么是合并提交,或者作为adjective修改单词“ commit”的“合并”的含义。不过,我们会像Git的好用户一样变得懒惰,并将其从“合并提交”缩短为“合并”,使其成为名词。
合并为名词
让我们先看看名词的用法,因为这很简单。在Git中,合并提交只是具有至少两个父提交的提交。真的就是这么简单。每次提交都有一定数量的父母。一个根提交有零个父提交(大多数存储库只有一个根提交,这是有史以来第一次提交,显然不能有一个父提交)。大多数普通提交只有一个父提交,而其他所有提交都有两个或多个父提交1,因此是合并提交。然后我们将短语“合并提交”缩短为“合并”,将“合并”从形容词(修改“提交”)更改为名词(意思是“合并提交”)。
如果合并提交是具有两个父级的提交,则git merge
必须进行合并提交,并且合并的意思是“进行合并提交”,并且确实如此,但有时是这样。我们将很快看到何时,更重要的是为什么。
1其他VCS可能会停在两个。例如,Mercurial这样做:没有hg commit拥有两个以上的父母。但是,Git允许任何提交有任何数量的父母。具有三个或更多父项的Git提交称为章鱼合并,通常这些提交使用git merge -s octopus
或同等功能进行,例如git merge topic1 topic2 topic3
:运行带有额外提交说明符的git merge
意味着-s octopus
。这些操作无法执行一系列使用双亲合并的操作,因此Git的章鱼合并不会比Mercurial拥有更多的功能,但是章鱼合并有时很方便,并且很好地展示了您的Git Fu。 :-)
合并为动词
合并的动词形式要复杂得多,或者至少在Git中。我们也可以区分两种主要的合并形式:合并源代码更改的行为,然后合并分支的行为。 (这带来了“什么是分支”的问题。这个问题比您想象的要多得多,因此请参见What exactly do we mean by "branch"?。)许多Git命令可以进行动词合并,包括git cherry-pick
和
git revert
的形式。2当然,
git apply -3
是最明显的实现方式,当这样做时,您会得到合并更改的动词形式。
合并变更
合并源更改更恰当地称为“三种方式合并”。有关更多信息,请参见
the Wikipedia article 3或
VonC's answer至
Why is a 3-way merge advantageous over a 2-way merge?。细节可能会变得非常复杂,但是此合并过程的目标非常简单:我们希望将对某个通用基础所做的更改组合在一起,即给定在某个起点B,我们找到更改C1和C2,并将它们组合在一起以形成一个新的更改C3,然后将该更改添加到基本B中以获得新版本。
在Git中,合并源(自己进行三向合并)的操作使用Git的索引,也称为“暂存区”。通常,每个文件的索引只有一个条目,它将进入下一个提交。当您
git merge
文件时,您告诉Git更新文件的暂存版本,替换当前索引中的版本,或者如果该文件以前是“未跟踪”的,则将该文件添加到索引中以便现在可以对其进行跟踪并准备下一次提交。但是,在合并过程中,每个文件的索引最多包含三个条目:一个来自合并基本版本,一个用于将文件版本视为更改#1(也称为“我们的”),另一个用于更改#2(“他们的”)。如果Git能够自己组合更改,则它将三个条目替换为一个常规(暂定提交)条目。否则,它将以冲突停止,并在文件的工作树版本中标记该冲突。您必须自己解决冲突(大概在此过程中编辑文件),并使用
git add
将三个特殊的冲突合并索引版本替换为一个正常的暂存版本。
解决所有冲突后,Git将能够进行新的提交。
进行合并提交
正常的合并制作
git add
要做的最后一件事是进行合并提交。同样,如果没有冲突,Git可以自己完成:
git merge
合并更改,
git merge
每个合并结果更新暂存文件,然后为您运行
git add
。或者,如果存在冲突,请解决它们,运行
git commit
,然后运行
git add
。在所有情况下,实际上都是
git commit
而不是
git commit
本身来进行合并提交。
最后一部分实际上非常简单,因为索引/暂存区都已设置。 Git像往常一样进行提交,除了不给它当前的(
git merge
)提交ID作为其单亲提交,而是给它至少两个父提交ID:第一个是与往常一样的
HEAD
提交。 ,其余部分来自
HEAD
(
git merge
)留下的文件。
2
.git/MERGE
中的
-3
(可以用
git apply -3
拼写)指示
--3way
在
git apply
输出中使用
index
字符串构造合并基础(如果需要)。在使用
git diff
和
git cherry-pick
进行此操作时,为了将它们转换为合并(而不是简单的补丁),Git会使用选择的或还原的提交的父提交结束。值得注意的是,在将补丁视为简单补丁失败之后,Git仅按文件执行此操作。仅当父提交是当前(HEAD)提交的祖先时,才使用父提交的文件作为三向合并的基本版本。如果实际上不是这样的祖先,则将“ base”到HEAD生成的差异与所应用的补丁结合起来可能没有帮助。不过,Git会作为备用。
3与Wikipedia一样,我现在发现其中存在一些小错误-例如,可能有两个以上的DAG LCA-但没有时间进行处理,而且它的概述也不是一件坏事。
4通常,它永远不会打扰首先要输入冲突的条目。如果可能的话,Git甚至可以缩短
git revert
阶段。例如,假设基本提交中有四个文件:
git diff
在任一提交中均与base保持不变,
u.txt
从base更改为
one.txt
,但未从base更改为另一个提交,
HEAD
从base提交到另一个提交,但不是从base提交到
two.txt
,并且两者都更改了
HEAD
。 Git会简单地直接复制
three.txt
,从
u.txt
中获取
one.txt
,从另一提交中获取
HEAD
,并且仅会产生差异,然后尝试将它们合并为
two.txt
。这进行得非常快,但这确实意味着,如果您对这些文件有自己的特殊三向合并程序,则永远不会为
three.txt
,
u.txt
和
one.txt
运行它,而只会为
two.txt
运行它。
我不确定在尝试合并差异之前还是尝试失败后,Git是否会创建多个索引条目。但是,在运行自定义合并驱动程序之前,它必须输入所有三个条目。
非合并“合并”
上面的序列-签出一些提交(通常是分支提示),在另一个提交(通常是其他分支提示)上运行
three.txt
,找到合适的合并基础,进行两组比较,组合比较,然后提交结果-普通合并的工作方式以及Git如何进行合并提交。我们合并(作为动词)更改,并进行合并(形容词)提交(或“进行合并”,名词)。但是,正如我们前面提到的,
git merge
并不总是这样做。
快进
有时
git merge
表示“正在进行快速合并”。这有点用词不当,因为在Git中,“快速转发”被更准确地视为分支标签更改的属性。还有两个使用此属性的命令
git merge
和
git fetch
,它们区分普通(或“快进”)分支更新和“强制”更新。正确讨论快速转发需要深入了解提交图,因此我在这里只说是将分支标签从提交O(旧)移动到提交N(新),并且提交N具有将O作为祖先。
当
git push
检测到您的merge参数是这些情况之一时-
git merge
是该另一提交的祖先-通常它将调用此快进操作。在这种情况下,Git仅使用您告诉它的提交来合并。根本没有新的提交,只是重新使用了一些现有的提交。 Git不会进行合并提交,也不会进行任何合并即动词。它只是将您切换到新提交,就像通过
HEAD
一样:移动当前分支标签并更新工作树。
您可以使用
git reset --hard
.5抑制此快进操作。在这种情况下,即使可以进行快进,
--no-ff
也会进行新的合并提交。您没有得到“动词合并”动作(没有工作要做),但是得到了新的合并提交,Git更新了您的工作树以进行匹配。
壁球合并不是合并
请注意,这里我们涵盖了三种情况中的两种:
动词形式加上形容词/名词形式:普通合并
合并的形容词/名词形式,但没有动词:可以快速转发但您运行
git merge
的合并
缺少的第三种情况是动词-无名词:我们如何获得合并的动作,结合更改,而没有合并提交的名词/形容词形式?这是“ squash merges”出现的地方。运行
git merge --no-ff
告诉Git照常执行合并操作,但不记录其他分支/ commit-ID,以便最终的
git merge --squash <commit-specifier>
进行正常的非合并,单亲提交。
就是这样,就是所有!最后,它只是进行普通的非合并提交。奇怪的是,它迫使您进行该提交,而不是自己进行。 (没有根本的理由必须这样做,而且我不知道Git作者为什么选择使其表现为这种方式。)但是,这些都是机制,而不是政策:它们告诉您如何进行各种调整。提交,而不提交您应该提交的内容,或者什么时候(最重要的是为什么)。
5您可以告诉
git commit
仅当它可以快速前进时才应继续:
git merge
。如果新提交是可快速转发的,则
git merge --ff-only
对其进行更新。否则,它只会失败。我创建了一个别名
git merge
来执行此操作,因为通常我要
git mff
然后查看是否需要合并,重新设置基址,完全创建一个新分支,还是其他。如果我可以快进,则无需执行其他任何操作,因此,如果
git fetch
有效,就可以完成。
何时,为什么使用哪种合并
为什么这个问题很难,并且像所有哲学问题一样,没有一个正确的答案(但肯定是一堆错误的答案:-))。考虑以下事实:每次完全使用
git mff
时,您可能会做一些不同的事情,并获得相同的源代码来进行最新的提交。
git merge
有三个成功的结果(也就是说,合并时您不必
git merge
结束它,而是成功地结束它):
快进:无新提交;来源是现有提交的来源。
真正的合并:新提交;源是组合源。
壁球合并:新提交;源是组合源。
这三个之间的唯一区别(除了第一个明显的“根本没有新的提交”)是它们在提交图中留下的记录。6快速前进显然不留下任何记录:该图与之前相比没有变化,因为您未添加任何内容。如果这是您想要的,那应该使用。在您正在跟踪别人工作而从不做任何事情的存储库中,这可能就是您想要的。这也是默认情况下得到的,Git将为您“工作”。7
如果执行常规合并,则将保留合并记录。所有现有的提交都保持原样,Git添加了一个新的提交,其中有两个父母。以后再走的任何人都会看到谁做了什么,何时,如何做等等。如果这是您想要的,这就是您应该做的。当然,某些工具(例如
git merge --abort
)将显示谁在做什么,何时做等等,这些操作通过显示所有历史的完整图片,可能会使所有细节都掩盖“大图片”视图。换句话说,这既是正面也是负面。
如果您执行壁球合并,则不会留下合并记录。您进行了一个新提交,以接过每个动词合并动作,但是新提交不是名词合并。以后再来的任何人都会看到所有工作,但看不到它来自何处。诸如
git log
之类的工具无法显示这些细节,您以及其他所有人将只能获得一幅全景图。同样,这既是正面也是负面。但是不利的一面也许更大,因为如果您发现以后需要这些细节,它们就不存在了。它们不仅不在
git log
视图中,而且在将来的
git log
中也不存在。
如果您永远不打算再进行压缩后的更改,这可能不是问题。如果您打算完全删除该分支,放弃所有单独的更改作为个人,而仅保留单个集体的南瓜合并更改,则执行
git merge
的“不良”部分的“不良值”实际上为零。但是,如果您打算继续处理该分支,然后再将其合并,则特定的不良值会大大增加。
如果要专门执行壁球合并,以使
git merge
输出“看起来更好”(显示更多的大图片而不是用太多的细节遮盖它),请注意,有多种
git merge --squash
选项旨在选择性地选择哪些提交表明。特别是,
git log
避免完全遍历合并的分支,只显示合并本身,然后继续沿提交图的“主线”向下移动。例如,您还可以使用
git log
忽略所有未标记的提交。
6好,也在您的致辞中;但是您的reflog是私有的,并且最终会过期,因此我们将忽略它们。
7这假设他们(无论是“他们”是)都不会通过重新设置基准或删除已发布的提交来“回退”他们的提交图。如果它们确实删除了已发布的提交,则默认情况下,您的Git将合并这些提交,就好像它们是您自己的工作一样。这是发布Git存储库的任何人都应该认真考虑“回退”此类提交的原因之一。
8假设没有章鱼合并。
关于git - git`merge --squash`不添加“Merge”头来提交,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39096500/
我从一个 Mercurial 存储库开始,它有多个我试图 merge 到其中的子存储库,就好像它们一直是主存储库的一部分一样。它们从一开始就不应该是子存储库。 我整理了一个将旧历史转换为单个存储库的过
假设我有一个主线分支和一个功能分支。我已经多次将主线分支 merge 到功能分支中,但只有少数非常小的 merge 冲突。我想清理历史,以便最后只有一个 merge 。执行此操作的最佳方法是什么? 最
首先我使用heapq.merge创建了a&b的两个结果,但是在mergea&b之后,我发现a的列表是空的。 >>> a=merge([1,2],[3,4]) >>> b=merge([4,5],[6,
我和我的团队正在使用远离主轨道 (origin/dev) 的远程分支 (origin/our_feature_branch) 开发一项功能。 Gerrit用于审查等。 使用 git merge ori
这个问题在这里已经有了答案: Is there a way to merge with Strategy "ours" without producing a new commit? (1 个回答)
GitLab 无法自动 merge 请求。所有 merge 请求都会收到消息“此 merge 请求包含必须解决的 merge 冲突。您可以在命令行上手动尝试” 消息似乎不正确,我通过使用“git br
git 有没有办法在不 merge 文件的情况下 merge 两个分支?换句话说就是绘制 merge 箭头。 假设我有分支 A 和 B。我需要将分支 B merge 到 A,但不需要 B 中的所有更改
我想使用提供 git 集成的流行的开源问题跟踪器 (Redmine)。不幸的是,跟踪器中的每个项目只能与一个 git repo 相关联。在跟踪器中创建多个项目不是我理想的设置。 考虑到这一点,我尝试使
在我们的存储库中,我们遵循基于 git-flow 的工作流程。我们有一个已完成的发布(安装在生产环境中),因此发布分支已 merge 到主分支中。 B---C---D---E [release
git merge 命令有一个执行快进 merge 的选项,但这不是我想要的,因为如果它不能执行快进 merge ,它会使用普通 merge . 是否有一个 git 命令仅执行快进 merge (从跟
尝试合并 TFS2008 时出现此错误。源分支或目标分支上都没有挂起的更改。 TF14083: The item {0} has a pending merge from the current me
为了更好地理解这些操作,我想知道 github 或 gitlab 到底是如何 merge 这些请求的。当压缩、 rebase 、 merge ......时详细执行哪些 git 命令? 最佳答案 PR
为了更好地理解这些操作,我想知道 github 或 gitlab 到底是如何 merge 这些请求的。当压缩、 rebase 、 merge ......时详细执行哪些 git 命令? 最佳答案 PR
我试图将提交的一部分从默认分支(不是所有文件和其他文件的部分) merge 到一个命名分支。我试过 graft ,但它只需要整个提交,而没有给我选择的机会。这将如何完成? 例子: A---B---C-
我正在进行 merge ,此时我已准备好提交,但我在 TortoiseHg 中的提交对话框显示许多文件已修改,但是当我与 parent 进行比较时,它说所有文件都是二进制相等的。 我没有也从未有过 e
我已经尝试了以下几种变体,但我仍然遇到错误。有什么办法可以解决这个问题。 DB2 10.1(DB2 for z/OS V10) 对于以下 MERGE INTO TRGT t USING SRC s O
我的数据库模型有用户和 MAC 地址。一个用户可以有多个MAC地址,但一个MAC只能属于一个用户。如果某个用户设置了他的 MAC,并且该 MAC 已经链接到另一个用户,则现有关系将被删除,并在新所有者
假设我有一个新功能,所以我创建了一个新分支。这个分支是一个会持续很长时间的副项目,所以我最终将 master merge 回它以使其保持最新状态。这已经发生了 50 次,因为我一直在更新它并消除该功能
过去几个小时我在 Mercurial 中进行了一次巨大的 merge 。 merge 131 个文件后,我的 merge 工具 meld 崩溃,显示 python 回溯。在尝试退出 meld 时,我无
我有一个关于 git merge 的问题。假设我的存储库中有两个分支(本地和远程):master 和 test。当我在测试分支上工作时,主分支被其他人更新了。在终端中,我写: git checkout
我是一名优秀的程序员,十分优秀!