Git 说分支已合并,但显然不存在更改

2024-02-14

我已经让自己陷入了一种对我来说毫无意义的境地。我会尽力描述它。

我有一个开发分支,我已通过以下方式将 master 合并到其中git checkout develpment && git merge master。我在这里没有遇到任何合并冲突。

有一个我感兴趣的特定提交,假设它是abcd123。当我跑步时git branch --contains abcd123,报告称两者development and master包含abcd123。当我做git log, 表明abcd123在提交列表中,都在development and on master.

git show abcd123显示包含对两个文件的更改。但我似乎找不到这些变化。当我查看文件时,我没有看到这些更改,也没有看到development nor on master。当我检查时git log -- path/to/file1,我没看到abcd123,同样对于git log -- path/to/file2.

这里发生了什么?怎么可能提交已经存在,但更改显然不存在?

它可能是abcd123最初是在另一个分支中引入的(除了development)被合并到master。我不知道这是否会有所作为。

顺便说一下,当我尝试时git checkout master && git merge development(合并后master into development如上所示)我收到很多合并冲突,包括 file1 和 file2。所以这似乎表明master实际上并没有合并到development——不应该git merge development成功如果git merge master已经被处决了吗?为了造成更多混乱,git branch --merged developmentmaster已合并到development。我想这符合git merge master ....

非常感谢此时的任何建议。

编辑:此时看来问题是由于合并失败或以某种方式搞砸了。如果有人仍在阅读,我认为托雷克的答案的方向似乎是最富有成果的。


这个答案很长,因为这里发生了很多事情。不过,TL;DR 摘要可能就是您想要的--full-history.

这里有多个单独的问题需要解决:

  • 短语“显示更改”,或者您在其中看到的内容git log -p or git show,经常会导致人们在解释 Git 存储的内容时走上错误的道路。
  • The git log命令有时会对你撒谎(尤其是在合并方面),纯粹是为了不让你被无用的信息淹没。
  • What git merge确实可能有点棘手。原则上很简单,但大多数人并不能立即理解。

普通提交

首先让我们看一下 Git 最常见的普通提交。在 Git 中,提交是一个snapshot(按文件名的文件内容)。您做出一项承诺,然后更改一些内容,然后git add更改一个或两个文件并进行第二次提交,第二次提交已所有相同的文件作为第一次提交,除了那些被git add.

值得将它们绘制为 Git 的一部分提交图。请注意,每个提交都有其自己唯一的哈希 ID(那些难以记住的字符串之一,例如93aefc or badf00d or cafedad),加上一个IDparent(或之前的)提交。父提交哈希让 Git 以向后的方式将这些东西串在一起:

... <-E <-F <-G ...

其中每个大写字母代表一个哈希 ID,箭头涵盖了每个提交“指向”其父级的想法。通常我们不需要绘制内部箭头(它们最终不是很有趣),所以我将它们绘制为:

...--E--F--G   <-- master

The name master然而,仍然值得一个箭头,因为该箭头指向的提交会随着时间的推移而改变。

如果我们选择像这样的提交G并查看它without using git log -p or git show,我们将完整地看到每个文件,就像它存储在提交中一样。事实上,这就是我们使用时发生的情况git checkout检查一下:我们将所有文件完整地提取到工作树中,以便我们可以查看并处理它们。但是当我们用git log -p or git show,Git 并没有向我们展示一切;它只告诉我们什么changed.

为此,Git 提取提交and它的父提交,然后运行一个大的git diff在这对上。无论父母之间有什么不同F和孩子G,就是这样changed,所以这就是git log -p or git show向您展示。

合并提交

这对于普通的单亲提交来说一切都很好,但它不适用于merge承诺。合并提交只是具有两个(或更多,但我们不会担心这种情况)父提交的任何提交。你可以通过成功地获得这些git merge,我们可以这样画。我们从两个分支开始(从某个起点分叉出来):

       H--I   <-- development (HEAD)
      /
...--E--F--G   <-- master

and then we run git merge master.1 Git now tries to combine the two branches. If it succeeds, it makes one new commit that has two parents:

       H--I--J   <-- development (HEAD)
      /     /
...--E--F--G   <-- master

名字development现在指向新的合并提交J。括号内的(HEAD)这里表示这是我们的当前分支。这告诉我们哪个name被移动:我们进行一个新的提交——包括任何新的合并提交——并且development是更改为指向新提交的分支名称。

如果我们不担心如何contents确定合并提交(各种提交的文件),这一切都非常简单。合并提交就像任何其他提交一样:它有一个完整的快照,一堆包含内容的文件。我们检查合并提交,这些内容像往常一样进入我们的工作树。

但是当我们去view合并提交……好吧,Git 通常会将提交与其父提交进行比较。合并有two父母,每个分支一名。 Git 应该对比哪一个来显示更改?

Here, git log and git show采取不同的方法。当您查看提交时git log, 表明什么都没有默认情况下。它不会选择I-vs-J,并且它不会选择G-vs-J任何一个!它什么也没显示,因为git log -p.


1In some Git workflows, merging from master into any other branch is discouraged. It can work, though, and since you did, let's run with it.


查看合并提交

The git show命令做了一些不同的、更好的事情。它运行two git diffs,一个用于I-vs-J和一个用于G-vs-J。然后它尝试combine两个差异,仅向您显示发生的变化both。也就是说,在哪里J不同于I但不是在一个特别的有趣的这样,Git 就抑制了差异。在哪里J不同于G但不是在一个特别的有趣的这样,Git 也抑制了这种差异。这可能是最有用的模式,所以就是这样git show显示。它仍然很不完美,但是你在这里所做的一切都不是完美的。

你(们)能做到git log通过添加来做同样的事情--cc to the git log -p选项。或者,您可以更改方式either git log or git show通过使用显示合并提交-m(注意用一个破折号表示-m, 两个为--cc, 顺便一提)。

The -m选项告诉 Git 为了查看目的,它应该split合并。现在Git比较I to J,向您展示通过合并带来的所有内容G。然后Git比较G到分离的额外版本J,向您展示通过合并带来的所有内容I。产生的差异通常非常大,但是(或因为)它向您展示了一切.

有更多方法可以尝试查找某些文件发生了什么,但我们需要稍等一下才能找到您的:

git log -- path/to/file1

命令。正如我们所看到的git log skipping合并,它可能会在这里跳过更多的事情(但有一些方法可以阻止 Git 这样做)。

实际进行合并:Git 如何构建合并的内容

让我们再看看合并前的图:

       H--I   <-- development (HEAD)
      /
...--E--F--G   <-- master

Here, there are two commits on branch development that are not on branch master, and two commits on master that are not on development. Commit E (along with all earlier commits) is on both branches. Commit E is special, though: it's the most recent2 commit that's on both branches. Commit E is what Git calls the merge base.

为了执行合并,Git 有效地运行两个git diff命令:

git diff E I
git diff E G

第一个对各种文件进行一组更改,这些更改是“我们在分支上所做的事情”development“。实际上,它是H and I如果它们被视为补丁。第二个对各种文件产生一组可能不同的更改,“我们在master“,和以前一样,它实际上是F and G作为补丁。

然后 Git 尝试合并这两个差异。它们之间完全独立的东西,需要both更改集,将它们应用于提交的内容E,并使用它作为结果。只要两个变更集触及同一行same文件中,Git 尝试查看它是否只能获取该更改的一份副本。如果两者都修正了文件第 33 行某个单词的拼写README, Git 可以直接取one copy的拼写修复。但是,只要两个更改集触及同一文件的同一行,就可以创建一个不同的更改时,Git 声明“合并冲突”,将其隐喻的双手举到空中,并使得you解决由此产生的混乱。

如果你(或任何进行合并的人)愿意,他们可以阻止 Git 提交合并结果,即使 Git 认为一切进展顺利:git merge --no-commit master使 Git 在合并所有内容后停止。此时,您可以在编辑器中打开工作树文件,change他们,写回他们,git add更改后的文件,以及git commit合并将并非来自合并的内容放入合并中any三个输入(基础和两个分支尖端)。

无论如何,理解这一切的关键是概念合并基地犯罪。如果您坐下来绘制提交图,合并基础通常非常明显,除非图失控(实际上这种情况经常发生)。您还可以让 Git 为您找到合并基础,或者在某些情况下为复数合并基础:

git merge-base --all master development

这会打印出哈希 ID。在我们假设的情况下,这将是提交的哈希 IDE。一旦你有了它,你就可以运行git diff手动查看每个文件发生了什么。或者您可以运行单独的文件git diff:

git diff E development -- path/to/file1
git diff E master -- path/to/file1

请注意,如果您替换名称master and development以及提交的哈希 IDwere当前的before你做了一个git merge,这甚至有效after合并。这会告诉你 Git 认为它应该结合起来做什么path/to/file1。反过来,这会告诉你 Git 是否没有看到更改,或者是谁进行了合并overrodeGit,或错误地处理了冲突合并。

一旦进行合并,后续合并将找到不同的合并基础:

       H--I--J----K   <-- development
      /     /
...--E--F--G--L--M   <-- master

现在,我们查看两个分支提示,并按照合并的两个分叉,向后追溯历史(向左方向)J,找到我们可以到达的第一个提交both分支提示。开始于K,我们回到J,然后对两者I and G。开始于M,我们回到L,然后到G。我们发现G在两个分支上,所以提交G是新的合并基础。 Git 将运行:

git diff G K
git diff G M

获取两个更改集以应用于合并基础提交G.


2"Most recent" here refers to commit graph order, rather than time stamps, although it's probably also the commit with the newest time stamp that is on both branches.


Git 日志和简化又是谎言

我们已经看到了git log -p只是跳过合并提交。你根本看不到任何差异,就好像合并完全是魔法一样。但是当你跑步时:

git log -- path/to/file1

还有一些更阴险的事情发生了。尽管相当不透明,但在(长)git log文档 https://www.kernel.org/pub/software/scm/git/docs/git-log.html在标题为历史简化 https://www.kernel.org/pub/software/scm/git/docs/git-log.html#_history_simplification.

在我们上面的例子中,假设git log正在步行从K向后。它发现J,这是一个合并。然后它会检查两者I and G,将每个比较J 排除除您正在查看的一个文件之外的所有文件后。也就是说,只是比较path/to/file1在三个提交中。

如果合并的一侧doesn't显示任何更改path/to/file1,这意味着合并结果J与输入没有什么不同(I or G)。 Git 称之为“TREESAME”。如果合并J,在被剥离到这个文件后,匹配I or G同样地精简,然后J是树一样的I or G(或者也许两者兼而有之)。在这种情况下,Git 选择 TREESAME 父级或任何一个 TREESAME 父级并查找only在那条路上。假设它选择了I(沿着顶行)而不是G(沿着底部)。

在我们的例子中,这意味着如果有人在合并过程中犯了错误,就会丢失本应进行的更改J from F, git log从不表现出来。这log命令查看K, then J,然后看着但掉落G,并且只看I, then H, then E,然后是任何早期的提交。它从不考虑提交F根本!所以我们看不到变化path/to/file1 from F.

这里的逻辑很简单;我将引用git log直接记录但添加一些强调:

[默认模式] 将历史记录简化为解释树的最终状态的最简单的历史记录。最简单,因为它修剪了一些侧枝如果最终结果相同 ...

自从发生变化以来F被删除,Git 声明它们不相关。你不需要见他们!我们将完全忽略合并的那一侧!

你可以完全击败这个--full-history。这告诉 Gitnot修剪合并的任一侧:它应该查看两个历史记录。这会发现提交F为你。添加-m -p还应该找到where更改已被删除,因为它会发现all涉及文件的提交:

git log -m -p --full-history -- path/to/file1

如果有更改(在提交中F在我们的示例中)并且不再存在,它们丢失的方式只有两种:

  • 它们被恢复(或者与git revert,或手动)在普通提交中。您会将此视为涉及的提交path/to/file1历史上即使没有-m; -p将向您展示差异。

  • 或者,它们在合并过程中因掉落而丢失。即使没有,你也会看到合并-m,但不确定是谁合并了这里的球;但-m -p将显示两个父级差异,包括应该有(但没有)接受零钱。

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

Git 说分支已合并,但显然不存在更改 的相关文章

  • 忽略 git 中的本地配置文件

    Rails 应用程序中有一些本地文件 属于我们存储库的一部分 我希望 git 忽略它们 基本上 我希望 git 忽略我对 config environments 目录和 config application rb 文件中的任何内容所做的所有
  • Jenkins git 插件 - 有时太慢

    以下内容摘自 Jenkins 日志 00 00 03 135 gt git fetch tags progress email protected cdn cgi l email protection some org some repo
  • refname 不明确且拉取失败

    我运行了以下命令 因为我想将生产分支移回而不必先签出 git branch f production HEAD 1 现在 当我检查生产时 我收到以下警告 warning refname production is ambiguous 然后我
  • 分支明显不同,但提交历史是相同的

    git status告诉我我的分支和我在另一个存储库上开始的分支已经分歧 On branch master Your branch and origin master have diverged and have 13 and 13 dif
  • git push heroku master 权限被拒绝

    我正在关注 ruby railstutorial 我运行命令 git push heroku master 它吐出了这个错误 Permission denied publickey fatal Could not read from rem
  • 如何 git grep 仅一组文件扩展名

    如何执行 git grep 并将检查的文件限制为一组文件 我希望能够 grep cpp 和 h 文件的内容来查找 MyFunc 例如 git grep MyFunc hc 但是 这也匹配 c 文件和 cs 文件 Use git grep M
  • Python 中的 Unix cat 函数 (cat * > merged.txt)? [复制]

    这个问题在这里已经有答案了 一旦建立了目录 有没有办法在Python中使用Unix中的cat函数或类似的函数 我想将 files 1 3 合并到 merged txt 我通常会在 Unix 中找到该目录 然后运行 cat gt merged
  • git 显示已添加到 gitignore 的文件中的更改?

    我已经将 log2 文件夹和 main js 文件添加到 gitignore 如屏幕截图所示 但即使执行后git rm cached r我仍然可以看到 git 正在检测 main js 和 log2 文件夹内文件的更改 怎么会 这些的常见问
  • Git:发送电子邮件而不提交

    我有一个项目 我做了更改 并想使用 git send email 功能将它们发送给另一个用户 我发现它可以通过发送补丁来工作 由git format patch每次提交 是否可以只发送diff的 我不想先提交 然后发送补丁 是否有gitfo
  • 第一次使用node.js - “ReferenceError:节点未定义”

    我刚刚安装了node js 我尝试编写应该检查版本的node v 但它不起作用 这是输出 gt node v ReferenceError node is not defined at repl 1 2 at REPLServer self
  • 默认情况下 git merge -Xignore-space-change

    我该如何设置该选项ignore space change对于所有合并使用git config 我也许可以使用别名merge 但因为我希望该设置应用于git stash pop git stash apply git pull and git
  • 无法从 Sourcetree 拉取 Git 远程存储库

    我生成了 ssh 密钥并配置了我的 git 和 SourceTree 我可以 git pull 并从 Git bash 执行其他操作 注意 我在 bashrc 中添加了以下内容以使其正常工作 eval ssh agent ssh add 然
  • 使用终端时 Git 推送在总计后卡住了?

    我尝试将一些文件推送到Github 总大小只有22 2M 我不知道为什么它在总行之后卡住了 我读过推送到 Github 时 Git 推送挂起 https stackoverflow com questions 16906161 git pu
  • apt-get 无法在 ubuntu dockerfile 中工作

    我对 docker 相当陌生 正在尝试通过编写自己的镜像来学习 并且目前正在阅读 Docker 的实际操作 ISBN 1633430235 在我自己的代码和书中的示例 第 146 页 中 我想通过 dockerfile 安装 git My
  • 在 Haskell 中合并两个列表

    无法弄清楚如何合并两个列表通过以下方式在哈斯克尔 INPUT 1 2 3 4 5 11 12 13 14 OUTPUT 1 11 2 12 3 13 4 14 5 我想提出一个更懒的合并版本 merge ys ys merge x xs y
  • 如何在 R 中合并同名列表中的数据框?

    我有一个包含很多数据框的列表 如果它们具有相同的名称 我想合并它们 即合并所有具有相同名称 a 和 b 的数据框 像这样 a lt aaaaa b lt bbbbb c lt ccccc g lt list df1 lt data fram
  • 当当前分支上有未提交的更改时签出另一个分支

    大多数时候 当我尝试签出另一个现有分支时 如果我在当前分支上有一些未提交的更改 Git 会不允许我这样做 所以我必须首先提交或隐藏这些更改 然而 有时 Git 确实允许我在不提交或存储这些更改的情况下签出另一个分支 并且它会将这些更改携带到
  • Git difftool 未启动外部 DiffMerge 程序

    我一直遵循 戴夫的博客条目 http www davesquared net 2009 05 setting up git difftool on windows html 链接在此answer https stackoverflow co
  • Android 存储库初始化失败

    我想我非常仔细地遵循该网站的说明 http source android com source downloading html http source android com source downloading html 但是当我尝试这
  • 通过 C# SqlCommand 执行合并语句不起作用

    我正在第一次尝试使用临时表和MERGE语句通过更新 SQL 表SqlCommandC 中的对象 我正在开发的程序旨在首先将大量记录 最多 20k 导出到 Excel 电子表格中 然后 用户可以搜索并替换特定值 并根据需要更新任意多记录中的任

随机推荐

  • 有没有办法控制默认使用哪个隐式转换?

    假设我有这个 class String2 val x String def times Int String val builder new StringBuilder for i lt 0 until times builder appe
  • SQL中调用动态函数名

    是否可以在 SQL 中调用具有动态名称的函数 例如 SELECT functionid SELECT results FROM dbo Function functionid AS results FROM List of Function
  • Jersey 2.x:如何在 RESTful 客户端上添加标头

    我已经看过了如何使用 Jersey Client API 在 RESTful 调用上添加标头 https stackoverflow com questions 18342456 how to add headers on restful
  • SQL Server 应用程序在 32 位和 64 位 SQL Server 版本之间的可移植性?

    我有一个当前正在针对 32 位 SQL Server 2005 标准版数据库运行的应用程序 由于我在此不赘述的原因 我需要将数据库移至在 64 位 Windows Server 2003 R2 Datacenter 上运行的 64 位 SQ
  • 使用 BouncyCastle 从文件中读取椭圆曲线私钥

    BouncyCastle 加密 API 允许使用常规方法创建和验证数字签名java security封装对象 例如java security PublicKey java security PrivateKey和他们的容器java secu
  • 我可以使用 socket.io-client 连接到标准 websocket 吗?

    尝试使用socket io client连接到用Go编写的websocket服务器 我已使用节点 WebSocket 库成功连接 npm https www npmjs org package ws 所以工作的 Websocket 代码如下
  • 解压一个类[重复]

    这个问题在这里已经有答案了 我想创建一个类来像字典一样解压它的对象 例如 使用字典 您可以这样做 foo a 1 b 2 def bar a b return a b bar foo outputs 3 我希望能够做到这一点 class F
  • “authenticate_user!”的设计实现在哪里?方法?

    设计实施在哪里authenticate user method 我一直在寻找 至今还没有找到 It s in lib devise controllers helpers rb1 https github com plataformatec
  • 如何将ListView分成多个部分并加载?

    我想知道我该如何划分我的ListView分成几部分 当用户启动我的应用程序时仅显示其中的一部分 并在用户按下名为 加载更多项目 的按钮时显示其他部分 我有一个包含 500 多个项目的大列表 并考虑将其分成几个部分 以便可以快速加载 我想要类
  • JQuery Datepicker:如果所选日期是今天的日期

    我可以使用以下方法确定所选日期是否是过去的日期 var due date due date val if new Date due date getTime lt new Date getTime do stuff 这工作正常 我使用以下内
  • 标签栏内存管理

    一个基于选项卡栏的应用程序 具有 5 个选项卡切换选项 我该如何有效地管理内存 选项卡之间的切换非常频繁我如何管理这种情况 or 无论如何 所有选项卡都会保持活动状态 但这是一个糟糕的选择 请详细描述有关标签栏内存管理的信息 让 UIKit
  • 在 VBA Excel 中复制和粘贴循环以实现多个输出

    所以我有以下 Excel 选项卡 代码 1 代码 2 代码 3 LI 2015 2016 2017 2018 2019 2015 年输出等 对于 2015 年 我在 2015 选项卡中有一个表 其中包含 10 行 3 个代码及其各自的 值的
  • 在按钮上移动手指时更改 textView

    在我的应用程序中 我有一个 Activity 其中有 9 个按钮和 1 个按钮textView 我正在利用OnTouchListener更改中的文本textView当我在按钮上移动手指时 This is what my activity l
  • linux C++ 串口回显输出

    在我的程序中 将字节发送到串行端口 我收到发送的字节 我不想接收我发送的字节 并且我不知道该怎么做 include
  • 使用 iframe 的内容在页面上执行 jquery .click()

    如果可能的话 我可以单击 iframe 中的某个元素并让它在呈现它的页面上执行某个功能吗 例如 div class page the source will render div class clickme div 同时 回到主页 div
  • 调试 javascript 中的闭包

    当我尝试调试有很多闭包的 JavaScript 代码时 我会设置断点 然后我去看堆栈 但大多数时候我只看到一个充满匿名函数的调用堆栈 这对我来说是一场噩梦 在 javascript 中调试闭包的最佳方法是什么 您可以为回调函数添加名称 这样
  • UISearchDisplayController 的正确实例化

    我做了一些搜索 但答案仍然不清楚 我正在尝试在 TableViewController TVC 内创建 UISearchDisplayController 的实例 在我的 TVC 的标题中 我将 searchDisplayControlle
  • 如何完全关闭 TBB 代码中的线程

    注意 这不是重复的这个问题 https stackoverflow com questions 3786408 number of threads used by intel tbb 给定一个与 TBB 并行的复杂软件 我如何完全关闭线程
  • VS2010中的vcbuild在哪里?

    我已经升级到 VS2010 正在寻找 vbuild 可执行文件 对于 VS2008 它位于 C Program Files x86 Microsoft Visual Studio 9 0 VC vcpackages vcbuild exe
  • Git 说分支已合并,但显然不存在更改

    我已经让自己陷入了一种对我来说毫无意义的境地 我会尽力描述它 我有一个开发分支 我已通过以下方式将 master 合并到其中git checkout develpment git merge master 我在这里没有遇到任何合并冲突 有一