git `merge --squash` 不会添加“合并”标头来提交

2023-11-25

我想知道所有工具如何知道合并哪些分支/提交,直到我在提交中找到“合并”标头。

我的问题是:为什么git merge --squash不添加该标头,同时git merge does?

换句话说:为什么我在合并时看到合并边缘git merge虽然没有边缘git merge --squash?

谢谢。

一些修改的信息:

对于“合并标题”,我的意思是第二行git log合并后:

commit 7777777
Merge: 0123456 9876543
Author: Some Body <...>
Date:   Fri ....

……同时git merge --squash will not生成该行,我的假设是 git gui 工具读取该标头以便能够绘制“合并边缘”,请参见下图。

我的问题又是一样的,见上文。


TL;DR: git merge --squash合并(动词),但不进行合并(名词)。

您提到的“合并标头”不在该形式的提交中。相反,它只是一些东西git log当遇到一个时打印合并提交。为了真正巩固这个想法,让我们看看什么是合并does——“合并”意味着什么verb——真是一次合并is。更准确地说,让我们看看什么是合并提交是,或者“合并”意味着什么形容词修改“提交”一词。不过,我们会像优秀的 Git 用户一样偷懒,并将其从“合并提交”缩短为“合并”,使其成为一个名词。

合并为名词

Let's look at the noun usage first, since that's simpler. In Git, a merge commit is simply a commit with at least two parent commits. It really is that simple. Every commit has some number of parents. A root commit has zero parents (and most repositories have only one root commit, which is the first commit ever made, which obviously cannot have had a parent commit). Most ordinary commits have just one parent, and all the other commits have two or more parents1 and are therefore merge commits. Then we shorten the phrase "merge commit" to just "a merge", changing "merge" from an adjective (modifying "commit") to a noun (meaning "a merge commit").

If a 合并提交是两个父母的承诺,那么git merge必须进行合并提交,并且to merge应该意味着“进行合并提交”——确实如此,但是只是有时。我们会看到when——更重要的是,why—soon.


1Other VCSes may stop at two. Mercurial does, for instance: no hg commit ever has more than two parents. Git, however, allows any commit to have any number of parents. A Git commit with three or more parents is called an octopus merge, and normally these commits are made using git merge -s octopus or equivalent, e.g., git merge topic1 topic2 topic3: running git merge with extra commit specifiers implies -s octopus. These do nothing that cannot be done using a series of two-parent merges, so Git's octopus merges do not give it more power than Mercurial, but octopus merges are sometimes convenient, and good for showing off your Git Fu. :-)


合并为动词

The verb form of to merge is much more complicated—or at least, it is in Git. We can distinguish between two major forms of merging as well: there's the act of merging source code changes, and then there's the act of merging branches. (This brings in the question of "what is a branch". There's a lot more to this question than you might think, so see What exactly do we mean by "branch"?) Many Git commands can do the verb kind of merging, including git cherry-pick and git revert, both of which are essentially a form of git apply -3.2 Of course git merge is the most obvious way to do it, and when it does, you get the verb form of merging changes.

合并变更

Merging source changes is more properly called "three way merging". For more about this, see the Wikipedia article3 or VonC's answer to Why is a 3-way merge advantageous over a 2-way merge? The details can get quite complex, but the goal of this merge process is simple enough: we want to combine changes made to some common base, i.e., given some starting point B, we find changes C1 and C2 and combine them to make a single new change C3, then add that change to the base B to get a new version.

In Git, the act of merging sources—of doing the three-way merge itself—uses Git's index, also called the "staging area". Normally, the index has just one entry for each file that will go into the next commit. When you git add a file, you tell Git to update the staged version of the file, replacing the one in the current index, or if the file was previously "untracked", adding the file to the index so that it is now tracked and also staged for the next commit. During the merge process, however, the index has up to three entries for each file: one from the merge base version, one entry for the file-version to be treated as change #1 (also called "ours"), and one for change #2 ("theirs"). If Git is able to combine the changes on its own, it replaces4 the three entries with one regular (staged-for-commit) entry. Otherwise it stops with a conflict, leaving the conflict marked in the work-tree version of the file. You must resolve the conflict yourself (presumably editing the file in the process), and use git add to replace the three special conflicted-merge index versions with the one normal staged version.

一旦所有冲突都解决了,Git 将能够进行新的提交。

进行合并提交

最后一件事是正常的合并git merge所做的就是进行合并提交。同样,如果没有冲突,Git 可以自行执行此操作:git merge合并更改,git adds 每个合并结果更新暂存文件,并运行git commit为你。或者,如果存在冲突,你可以解决它们,you run git add, and you run git commit。在所有情况下,实际上都是git commit, 而不是git merge本身,这使得合并提交。

最后一部分实际上非常简单,因为索引/暂存区域都已设置完毕。 Git 只是像往常一样进行提交,只不过不是给它当前的 (HEAD) 提交的 ID 作为其单亲,它至少给出了它two父提交 ID:第一个是HEAD像往常一样提交,其余的来自于留下的文件git merge (.git/MERGE).


2The -3 in git apply -3, which can be spelled out as --3way, directs git apply to use the index string in git diff output to construct a merge base if needed. When doing this with git cherry-pick and git revert, to turn them into merges (instead of straightforward patches), Git winds up using the parent commit of the cherry-picked or reverted commit. It's worth noting here that Git does this only on a per file basis, after treating the patch just as a simple patch has failed. Using the parent commit's file as a base version for a three-way merge will normally help only if that commit is an ancestor of the current (HEAD) commit. If it's not actually such an ancestor, combining the diff generated from "base" to HEAD with the patch being applied is probably not helpful. Still, Git will do it as a fallback.

3As usual for Wikipedia, I spotted some minor inaccuracies in it just now—for instance, it's possible to have more than two DAG LCAs—but don't have time to work on it, and it's not a bad overview.

4Often, it never bothers to make the conflicted entries in the first place. Git will, if possible, short-cut-away even the git diff phase. Suppose for instance that the base commit has four files in it: u.txt is unchanged from base in either commit, one.txt is changed from base to HEAD but not from base to the other commit, two.txt is changed from base to the other commit but not from base to HEAD, and three.txt is changed in both. Git will simply copy u.txt straight through, take one.txt from HEAD, take two.txt from the other commit, and only bother to generate diffs, then try to merge them, for three.txt. This goes pretty fast, but does mean that if you have your own special three-way-merge program for these files, it never gets run for u.txt, one.txt, and two.txt, only for three.txt.

我不确定 Git 是否在尝试合并差异之前或尝试失败后创建多个索引条目。但是,在运行自定义合并驱动程序之前,它必须创建所有三个条目。


非合并“合并”

上面的序列 - 检查一些提交(通常是分支提示),运行git merge在另一个提交上(通常是一些other分支提示),找到合适的合并基础,制作两组差异,合并差异,然后提交结果——这就是正常合并的工作方式,以及 Git 进行合并提交的方式。我们合并(作为动词)更改,并进行合并(形容词)提交(或“进行合并”,名词)。但是,正如我们之前指出的,git merge并不总是这样做。

快进

有时git merge说它正在“进行快进合并”。这有点用词不当,因为“快进”更准确地被认为是分支标签更改,在 Git 中。还有另外两个命令使用此属性,git fetch and git push,它区分正常(或“快进”)分支更新和“强制”更新。对快进的正确讨论需要深入了解提交图的细节,所以我在这里要说的是,当您从提交中移动分支标签时,会发生这种情况O(旧)承诺N(新),并提交N已提交O作为祖先。

When git merge检测到您的合并参数是这些情况之一 - 即HEAD是另一个提交的祖先——它通常会调用这个快进操作。在这种情况下,Git 仅使用您告诉它进行合并的提交。没有new完全提交,只是重用一些existing犯罪。 Git 不进行合并提交,也不进行任何合并作为动词。它只是将您更改为新的提交,几乎就像git reset --hard:移动当前分支标签并更新工作树。

You can suppress this fast-forward action with --no-ff.5 In this case, git merge will make a new merge commit even if a fast-forward is possible. You get no merge-as-a-verb action (there's no work to do) but you do get a new merge commit, and Git updates your work-tree to match.

挤压合并不是合并

请注意,我们在这里讨论了三种情况中的两种:

  • 动词形式加形容词/名词形式:正常合并
  • 合并的形容词/名词形式,但没有动词:可以快进的合并,但你跑了git merge --no-ff

缺少的第三种情况是动词没有名词:我们如何得到action合并的意思,合并更改,没有合并的名词/形容词形式commit?这就是“挤压合并”的用武之地。git merge --squash <commit-specifier>告诉 Git 像往常一样执行合并操作,但是not记录其他分支/commit-ID,以便最终git commit进行正常的、非合并的单父提交。

真的就是这样——这就是它的全部作用!它只是在最后进行正常的非合并提交。奇怪的是,它迫使you做出承诺,而不是自己做出承诺。 (没有根本原因表明它必须这样做,而且我不知道为什么 Git 作者选择让它这样做。)但这些都是机制, not policies: 他们告诉你how进行各种提交,但不包括您要提交的提交should制造,或者何时,或者——最重要的是——why.


5You can tell git merge that it should only proceed if it can fast-forward: git merge --ff-only. If the new commit is fast-forward-able, git merge updates to it. Otherwise it simply fails. I made an alias, git mff, that does this, since normally I want to git fetch and then see whether I need to merge, rebase, make a new branch entirely, or whatever. If I can fast-forward, I don't need to do anything else, so if git mff works, I'm done.


使用哪种合并、何时使用、为什么使用

The why这个问题很难,就像所有哲学问题一样,没有一个正确的答案(但肯定是一堆错误的:-))。考虑这个事实:每次你使用git merge无论如何,你could做了不同的事情却得到了相同的结果源代码与您最新的提交一起进行。成功的结果有以下三种git merge(也就是说,您不进行合并git merge --abort结束它,而是成功结束它):

  • 快进:没有新的提交;源是现有提交的源。
  • 真正的合并:新提交;源是组合源。
  • 挤压合并:新提交;源是组合源。

The only difference between these three (aside from the obvious "no new commit at all" for the first one) is the record they leave behind in the commit graph.6 A fast-forward obviously leaves no record: the graph is unchanged from before, because you added nothing. If that's what you want, that's what you should use. In a repository where you are following someone else's work and never doing anything of your own, this is probably what you want. It is also what you will get by default, and Git will "just work" for you.7

If you do a regular merge, that leaves a record of the merge. All the existing commits remain exactly as they are, and Git adds one new commit, with two8 parents. Anyone coming along later will see just who did what, when, how, etc. If this is what you want, this is what you should do. Of course, some tools (like git log) will show who did what, when, etc., which—by showing a complete picture of all of history—may obscure the Big Picture view with all the little details. That's both the up-side and the down-side, in other words.

如果你进行挤压合并,剩下no合并记录。您进行一个新的提交,将每个合并作为动词action,但新的提交不是名词合并。后来的任何人都会看到所有进入的工作,但看不到它来自哪里。类似的工具git log cannot展示小细节,你和其他人都会得到only大图景。同样,这既有好处也有坏处。但缺点可能更大一点,因为如果你发现你need稍后这些细节,它们是不在那里。他们不仅不存在于git log看来,他们也不存在未来git merge.

如果你是never要做一个未来git merge压缩的变化,这可能不是问题。如果您打算完全删除该分支,放弃所有单独的更改as个人并只保留单个集体挤压合并变化,这是做的“坏”部分git merge --squash“坏值”基本上为零。但是,如果您打算继续在该分支上工作,并稍后再次合并它,则该特定的不良值会大大增加。

如果您专门进行壁球合并以制作git log输出“看起来更好”(显示更多的大图,而不是用太多细节来模糊它),请注意,有各种git log选项设计为可选择的 about 哪个承诺表明。尤其,--first-commit避免完全遍历合并的分支,仅显示合并本身,然后继续沿着提交图的“主线”向下。您还可以使用--simplify-by-decoration省略除tagged例如,提交。


6Well, also in your reflogs; but your reflogs are private, and eventually expire, so we'll just ignore them.

7This assumes that they—whoever "they" are—do not "rewind" their commit graph, by rebasing or removing published commits. If they do remove published commits, your Git will by default merge those commits back in, as if they were your own work. This is one reason anyone publishing a Git repository should think hard about "rewinding" such commits.

8Assuming no fancy octopus merges.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

git `merge --squash` 不会添加“合并”标头来提交 的相关文章

  • 名称和电子邮件在 Git 的每用户配置文件中设置,但 Git 仍使用默认生成的名称和电子邮件

    标题已经说了 但我会更彻底地解释一下 我已使用以下命令按照建议配置了用户名和电子邮件 git config global user name git config global user email 我可以通过执行以下操作来验证这是设置的g
  • git update-index --no-assume-unchanged 不起作用

    我设置了assume unchanged咬了几个文件 现在我想取消它们 但这不起作用 gt git update index no assume unchanged Gemfile gt git ls files v grep Gemfil
  • Git 与人工制品

    刚刚完成 NPM 和 Bower 的 Artifactory 设置 它非常容易使用 您只需更改存储库 URL 一切就正常了 查看有关如何让 Artifactory 与 github vcs 一起使用的文档 它看起来过于复杂 我想知道是否有人
  • 如何与其他用户共享 bitbucket 存储库?

    我正在使用 Bit 存储桶 并且我想与一位朋友分享我的存储库 我用的是免费的个人账户 似乎有一个选项可以在位桶中创建团队 但它说它将把我的帐户从个人帐户转换为团队帐户 我不要那个 我如何授予其他用户访问此存储库的权限 有一个共享链接选项 如
  • Git子模块绝对工作树路径配置

    这是我的子模块redmine 仪表板配置文件 子模块配置文件 core repositoryformatversion 0 filemode true bare false logallrefupdates true worktree Us
  • 合并来自并行 Subversion 存储库的更新代码

    我想知道人们在将实时 持续更新的 SVN 存储库与离线存储库 更新频率较低 合并时通常会采取哪些步骤 以我的场景为例 不久前我下载了BugTracker NET系统 当时它的版本是3 2 3 随后我对我们业务的代码进行了一些增强 我们使用
  • 如何根据查找表匹配多列

    我有以下两个数据框 lookup lt data frame id c A B C price c 1 2 3 results lt data frame price 1 c 2 2 1 price 2 c 3 1 1 我现在想要浏览所有列
  • 如何使用 git-tfs 清理损坏的历史记录

    我不确定我是如何进入这种状态的 但我的 tfs 默认远程分支中有一些 TFS 中不存在的提交 所以我想摆脱它们 所以我的历史是这样的 A B C D tfs default 但提交 B 和 C 实际上并不是 TFS 中的变更集 因此 当我签
  • git tag --contains 如何工作?

    从几天前开始 我一直在尝试确定在哪个版本 由标签指定 中部署了特定修复 由提交指定 这里的一些帖子发布了这一点git tag contains是收集这些信息的方法 但是选项的文档 https git scm com docs git tag
  • 如何摆脱 gerrit 中的错误依赖关系

    看起来 当使用 gerrit 时 默认情况下所有更改都依赖于前一个更改 我不会分支进行新的更改 我只是在主分支上工作 然后将提交的更改推送到远程源 主分支 即使两次提交彼此无关 每次都会创建依赖关系 我遇到了一些问题 这让我认为我没有正确地
  • 如何在 git 交互式 rebase 中将提交与下一个提交合并?

    git rebase i允许通过以下方式将提交与前一个提交合并squash or fixup 这两个选项都需要至少一次提交pick ed 当一个人想要使用第一个提交但丢弃其提交消息时 情况怎么样 换句话说 如果我希望第一个提交与后续提交合并
  • 避免使用 git 和 make 重新编译

    我在 git 中有两个开发分支 并且经常需要在两者之间进行更改 然而 真正令人沮丧的是 每次我在 git 中更改分支时 整个项目都会重新构建 因为某些文件的文件系统时间戳会发生变化 Ofc makefiles 配置为将项目构建到两个不同的构
  • 如何使用 git 撤消所有空白更改

    我有一个 git 存储库 我在其中本地替换了很多文件 git status 现在显示许多修改的文件 有些是 真正修改过的 其他的只是行结尾不同 我希望那些仅通过行结尾不同的内容消失 git重置它们 但是 我似乎找不到 linux pipin
  • git checkout HEAD -- 和有什么区别?和 git reset --hard HEAD?

    我已经查看了这个 stackoverflow 链接 但我认为我所要求的之间的细微差别是使用HEAD在结账命令中 因为他们的建议似乎不起作用 git reset hard HEAD 和 git checkout 之间有区别吗 https st
  • 更改 Windows 安装的 Git Bash 中 ~ 目录的位置

    我什至不确定我问的是正确的问题 让我解释一下我的情况 这是关于 Windows 7 上的 Git 我的公司在网络驱动器上设置 Windows 用户目录 而不是在本地硬盘驱动器上 用于备份和超出本问题范围的其他目的 我无法改变这项政策 然而
  • 设置单独的遥控器以仅推送和拉动子文件夹

    假设我有两个存储库 repo A and repo B 其中包含一个文件夹 其中的代码与存储库 A 中的代码类似 这实际上如何发生并不重要 但是好吧 让我们假设我刚刚从 A 复制了内容 现在我想要以下内容 就像我在 repo A 中习惯的那
  • Git 和重定向

    我注意到 当 git html 项目页面如下所示 https github com fruux sabre dav 被克隆 以这种方式 git clone https github com fruux sabre dav 实际生成的远程 U
  • R - 通过合并和超过 2 个后缀进行减少(或者:如何合并多个数据帧并跟踪列)

    我正在尝试基于 2 列合并 4 个数据帧 但要跟踪列源自哪个数据帧 我在跟踪列时遇到问题 参见 dput dfs 帖子末尾 df example df1 Name Color Freq banana yellow 3 apple red 1
  • 是否有 git-merge --dry-run 选项?

    我正在合并一个可能有很多冲突的远程分支 我怎么知道它是否会发生冲突 我没有看到任何类似的东西 dry run on git merge 如前所述 传入 no commit标志 但为了避免快进提交 也传入 no ff 像这样 git merg
  • git 索引到底包含什么?

    Git 索引到底包含哪些内容 可以使用什么命令查看索引内容 感谢您的所有回答 我知道索引充当暂存区 提交的内容是在索引中而不是工作树中 我只是好奇索引对象由什么组成 我猜它可能是文件名 目录名 SHA 1 对的列表 也许是一种虚拟树 在 G

随机推荐

  • 为什么书上说“编译器在内存中为变量分配空间”?

    为什么书上说 编译器在内存中为变量分配空间 这不是可执行文件吗 我的意思是 例如 如果我编写以下程序 include
  • 无法循环打开 png 设备

    我一直在摆弄 R 中的一个函数 长话短说 我有一个for loop 在每一步 我使用保存一个图png 然后立即readPNG这样我就可以提取RGB信息 然后我制作第二个情节 然后readPNG这样我就可以比较两个图像的 RGB 问题是我不断
  • Snowflake (LEFT JOIN) LATERAL:无法评估不支持的子查询类型

    横向连接 在 FROM 子句中 LATERAL 关键字允许内联视图引用该内联视图之前的表表达式中的列 横向连接的行为更像是相关子查询 而不是大多数连接 让我们稍微调整一下文档中提供的代码 CREATE TABLE departments d
  • 导入错误:未找到 MagickWand 共享库 [windows]

    早上好 经过多次尝试运行 from wand image import Image 我收到以下错误 Traceback most recent call last File C Users XXXXX PycharmProjects PDF
  • Botframework:如何使用机器人处理长时间运行的任务?

    如何处理机器人上长时间运行的任务 以便客户端不会在 15 秒后再次尝试发送消息 我有一个带有 botframework v3 的机器人 并通过直线连接客户端 The 直达专线通道连接器本身不会重试发送消息 如果它在向您的机器人发送消息后 1
  • 获取 .NET 对象的内存地址 (C#)

    我试图追踪单声道运行时中的一个错误 其中一个变量似乎分配给一个有效对象 然后稍后重新分配给一个虚假对象 特别是 early in code I allocate fine var o new object valid allocation
  • 两个 ddev 项目之间的通信

    我有两个需要相互交互的 ddev 项目 当遇到一些问题时 我会检查连接的已解析 IP 我通过 ssh 进入 project1 并 ping project2 来完成此操作 ping project2 ddev local 域名解析为 127
  • Spring security oauth 2简单示例

    我尝试根据官方教程实现我自己的示例Sparklr2 Tonr2 一切看起来都不错 但是当我从web xml in my Tonr2实现 弹簧安全过滤器我有例外 尚未为当前请求建立重定向 URI 我不明白我应该使用什么 URL 这是我的代码
  • 凹边界半径可以吗?

    这是一个简单的凸示例 http jsfiddle net swY5k test width 200px height 200px background 888888 border radius 50px 但是 我想要一个凹形边界半径 我尝试
  • jQuery .each css 不是一个函数

    我有一个包含 3 个成员的 jQuery 对象 var elements this wrapperName gt ul gt li gt a gt img Object 0 img 1 img 2 img length 3 prevObje
  • 如何将正则表达式转换为字符串文字并再次转换回来?

    我怎么能够 将带有标志的 JavaScript RegExp 转换为字符串文字 想想 JSON 并将该文字转换回正则表达式 例如使用字符串 the weather is nice today var myRe new RegExp weat
  • Android 位置管理器标准

    我需要从网络和 GPS 提供商处接收位置更改 如果 GPS 提供商不可用或没有位置 基于卫星可见性 我将从网络提供商处接收位置 否则从 GPS 提供商处接收位置 是否可以根据我的需要使用标准选择提供商 实际上Android 开发者 让您的应
  • 通过 URL 运行自动化脚本

    马克西莫 7 6 1 1 我想通过调用单独系统中的 URL 来运行 Maximo 自动化脚本 是否有可能做到这一点 这是一个很好的用例 也是我们过去几天一直在努力解决的问题 创建自动化脚本 我的叫做automation api test 使
  • 如何使用Akka-HTTP客户端websocket发送消息

    我正在按照以下文档尝试客户端 WebsocketwebSocket客户端流 示例代码是 import akka actor ActorSystem import akka Done import akka http scaladsl Htt
  • Spring Boot 安全身份验证 - 302 重定向

    我正在尝试测试使用标准 Spring Security API 保护的 Web api 我通过实现 UserDetailService 实现了自己的用户身份验证服务 然而 每当我登录到我的应用程序时 login api 都会返回 302 重
  • Mp4 视频无法在 iPad 中播放

    美好的一天 我一直在致力于这个项目并学习如何在 iPad 和所有其他浏览器上放置视频 但在为此编写代码后 我注意到我从 iPad 获得的唯一信息是视频的第一个关键帧 但视频没有播放 当我按下 iPad 屏幕上出现的 播放 按钮时 视频不会播
  • 如何使用简单注入器、存储库和上下文 - 代码优先

    我正在尝试使用 Simple Injector 创建我的存储库并在业务逻辑层中使用它 我也想使用 PerWebRequest 方法 在 DAL 层我有 public interface IRepository
  • Laravel 5. 调试模式

    我将调试模式设置为true在 config gt app 中并将其部署到服务器上 debug gt env APP DEBUG true 我在控制器中有以下代码来检查模式 debug config app debug var dump de
  • Android 媒体录制:java.lang.RuntimeException:启动失败

    我正在处理电话录音 当我开始录制电话时 不幸的是它停止了 它给出了错误MediaRecorder启动失败 2147483648 我参考这个答案link 但我不明白 请告诉我我的代码有什么问题 这是我的代码 public class Inco
  • git `merge --squash` 不会添加“合并”标头来提交

    我想知道所有工具如何知道合并哪些分支 提交 直到我在提交中找到 合并 标头 我的问题是 为什么git merge squash不添加该标头 同时git merge does 换句话说 为什么我在合并时看到合并边缘git merge虽然没有边