gpt4 book ai didi

Git表示分支已 merge ,但显然不存在更改

转载 作者:行者123 更新时间:2023-12-04 13:55:31 25 4
gpt4 key购买 nike

我已经把自己弄成了对我来说没有道理的情况。我会尽力描述它。

我有一个开发分支,并且已经通过git checkout develpment && git merge master将master merge 到了其中。我在这里没有任何 merge 冲突。

我感兴趣的是一个特定的提交,假设它是abcd123。当我运行git branch --contains abcd123时,它报告developmentmaster都包含abcd123。当我执行git log时,它将在abcd123development上的提交列表中显示master
git show abcd123显示包含对两个文件的更改。但是我似乎找不到这些变化。当我查看文件时,无论是development还是master都没有看到这些更改。当我检查git log -- path/to/file1时,没有看到abcd123,与git log -- path/to/file2相同。

这里发生了什么?如何提交,但更改显然不存在?
abcd123可能最初是在 merge 到development的另一个分支(master除外)中引入的。我不知道这是否可以有所作为。

顺便说一句,当我尝试git checkout master && git merge development时(如上图所示将master merge 到development之后),我遇到很多 merge 冲突,包括file1和file2。因此,这似乎表明master实际上并未 merge 到development中-如果已经执行了git merge development,那么git merge master是否不应该成功?为了引起更多的困惑,git branch --merged development表示master已 merge 到development中。我想这与git merge master一致..

在这一点上的任何建议,我们将不胜感激。

编辑:在这一点上,问题似乎是由于 merge 失败或以某种方式弄乱了。如果仍然有人在阅读,我认为torek的答案所走的方向似乎是最富有成果的。

最佳答案

这个答案很长,因为这里有很多事情要做。但是,TL; DR摘要可能是您需要--full-history

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

  • 短语“显示更改”,或者您在git log -pgit show中看到的内容,通常会导致人们在解释Git存储的内容时走错了路。
  • 有时会向您说谎git log命令(尤其是在 merge 周围),纯粹是为了不让您浪费无用的信息。
  • git merge的功能可能有些棘手。从原理上讲这很简单,但是大多数人不会马上得到它。

  • 普通提交

    首先让我们看一下Git最常见的普通提交。在Git中,提交是快照(按文件名显示文件内容)。进行一次提交,然后进行一些更改,然后将一个或两个更改的文件 git add进行第二次提交,第二次提交与第一次提交具有相同的文件,但 git add覆盖的文件除外。

    值得将它们作为Git提交图的一部分来绘制。请注意,每个提交都有其自己唯一的哈希ID(那些无法记住的字符串之一,例如 93aefcbadf00dcafedad),以及父(或上一个)提交的ID。父提交哈希让Git以反向方式将这些东西串在一起:
    ... <-E <-F <-G ...

    其中每个大写字母代表一个哈希ID,而箭头则涵盖了每个提交都“指向”其父对象的想法。通常我们不需要画内部箭头(最后它们不是很有趣),因此我将它们绘制为:
    ...--E--F--G   <-- master

    但是,名称 master仍应使用箭头,因为该箭头指向的提交将随时间变化。

    如果我们选择像 G这样的提交并在不使用 git log -pgit show的情况下进行查看,则将看到完整的每个文件,与存储在提交中的文件完全相同。实际上,这就是我们使用 git checkout进行 check out 时发生的情况:我们将所有文件全部提取到工作树中,以便我们可以查看和处理它们。但是,当我们使用 git log -pgit show对其进行查看时,Git不会向我们显示所有内容。它只告诉我们发生了什么变化。

    为此,Git提取提交及其父提交,然后在该对上运行一个大 git diff。父 F和子 G之间的区别是什么,那就是更改了,因此 git log -pgit show向您显示了什么。

    merge 提交

    对于普通的单亲提交,这一切都很好,但是对于 merge 提交却不起作用。 merge 提交只是具有两个(或更多,但我们不会担心)父提交的任何提交。您可以通过成功完成 git merge来获得这些代码,我们可能会这样绘制。我们从两个分支(从某个起点 fork )开始:
           H--I   <-- development (HEAD)
    /
    ...--E--F--G <-- master

    然后我们运行 git merge master .1 Git现在尝试 merge 两个分支。如果成功,它将提交一个新的提交,该提交具有两个父级:
           H--I--J   <-- development (HEAD)
    / /
    ...--E--F--G <-- master

    现在,名称 development指向新的 merge 提交 J。括号中的 (HEAD)表示这是我们当前的分支。这告诉我们移动了哪个名称:我们进行新的提交(包括任何新的 merge 提交),并且 development是更改为指向新提交的分支名称。

    如果我们不担心如何确定 merge 提交的内容(各种提交的文件),那么这很简单。 merge 提交与其他任何提交一样:具有完整的快照,一堆包含内容的文件。我们 check out merge 提交,这些内容照常进入我们的工作树中。

    但是,当我们去查看 merge 提交时...好吧,Git通常会将提交与父提交进行比较。 merge 有两个父级,每个分支一个。 Git应该与哪一个进行比较以显示您的变化?

    在这里, git loggit show采用不同的方法。当您使用 git log查看提交时,默认情况下它什么也不显示。它不会选择 I -vs- J,也不会选择 G -vs- J!对于 git log -p,它什么也没有显示。

    1在某些Git工作流程中,不建议从master merge 到任何其他分支。但是它可以工作,并且既然您做到了,就让我们运行它。

    查看 merge 提交
    git show命令可以做一些不同的事情,并且效果更好。它运行两个 git diff,一个用于 I -vs- J,一个用于 G -vs- J。然后,它尝试 merge 两个差异,仅向您显示两个差异。也就是说,在 JI不同但没有特别有趣的方式的情况下,Git抑制了这种差异。在 JG不同但没有特别有趣的方式的地方,Git也抑制了这种差异。这可能是最有用的模式,这就是 git show显示的内容。它仍然是不完美的,但是您在这里无法做的所有事情都是完美的。

    您可以通过将 git log添加到 --cc选项来使 git log -p做到这一点。或者,您可以通过使用 git log更改 git show-m显示 merge 提交的方式(顺便说一下,对于 -m,请注意一个破折号,对于 --cc,请注意两个破折号)。
    -m选项告诉Git,出于查看目的,它应该拆分 merge 。现在,Git将 IJ进行比较,以向您展示您通过 merge G带来的一切。然后,Git将 G与拆分后的 J额外版本进行比较,以向您展示通过 merge I带来的所有内容。产生的差异通常很大,但是(或因为)它可以显示所有内容。

    有更多方法尝试查找某个文件发生了什么,但是在进入您的文件之前,我们需要稍等片刻:
    git log -- path/to/file1

    命令。就像我们看到 git log跳过 merge 一样,它可能在这里跳过更多的事情(但是有一些方法可以阻止Git这样做)。

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

    让我们再次查看该 merge 前的图形:
           H--I   <-- development (HEAD)
    /
    ...--E--F--G <-- master

    在这里,在分支 development上有两个提交不在分支 master上,并且在 master上有两个提交不在 development上。提交 E(以及所有先前的提交)在两个分支上。但是, E的提交是很特殊的:这是两个分支上的最新提交。提交 E是Git所谓的 merge 基础。

    为了执行 merge ,Git有效地运行两个 git diff命令:
    git diff E I
    git diff E G

    第一个对各种文件进行了一组更改,即“我们对分支 development所做的操作”。实际上,如果将它们视为补丁,则为 HI的总和。第二个对不同文件产生了一组可能不同的更改集,“我们对 master所做的事情”,并且像以前一样,它实际上是作为补丁的 FG的总和。

    然后,Git尝试 merge 这两个差异。无论它们之间是什么完全独立的,它都会接受两组更改,并将它们应用于commit E的内容,并将其用作结果。无论两个变更集在同一文件中的哪一行接触到同一行,Git都会尝试查看它是否只能获取该变更的一个副本。如果两者都修复了文件 README的第33行上的单词的拼写,那么Git可以只获取一份拼写修复程序。但是,无论这两个变更集在同一文件的同一行上发生什么变化,但做出不同的变更,Git都会声明“merge 冲突”,将其隐喻性的举动悬而未决,并让您修复所产生的困惑。

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

    无论如何,理解所有这些的关键是 merge 基础提交的概念。如果您坐下来绘制提交图,则 merge 基础通常非常明显,除非该图失去控制(实际上发生的次数很多)。您还可以让Git为您找到 merge 基础,或者在某些情况下 merge 基础(复数形式):
    git merge-base --all master development

    这将打印出哈希ID。在我们的假设情况下,这将是commit E的哈希ID。一旦有了它,就可以手动运行 git diff,以查看每个文件发生了什么。或者,您可以运行单个文件 git diff:
    git diff E development -- path/to/file1
    git diff E master -- path/to/file1

    请注意,如果您用 master之前的最新提交的哈希ID替换名称 developmentgit merge,即使在 merge 后也可以使用。这将告诉您Git认为应该为 path/to/file1组合什么。反过来,这将告诉您Git是否未看到更改,或者进行 merge 的人是否取代了Git,或是否错误地处理了冲突的 merge 。

    merge 后,后续 merge 将找到其他 merge 基础:
           H--I--J----K   <-- development
    / /
    ...--E--F--G--L--M <-- master

    现在,我们来看两个分支技巧,并按照历史记录(向左方向)向后追溯,沿着 J之类的 merge 的两个分支,找到我们可以从两个分支技巧中进行的第一次提交。从 K开始,我们回到 J,然后回到 IG。从 M开始,我们回到 L,然后回到 G。我们发现 G在两个分支上,因此commit G是新的 merge 基础。 Git将运行:
    git diff G K
    git diff G M

    获取两个更改集以应用于基于 merge 的提交 G

    2这里的“最新”是指提交图的顺序,而不是时间戳,尽管这可能是两个分支上具有最新时间戳的提交。

    Git日志和简化之处再次出现

    我们已经看到 git log -p只是跳过 merge 提交。您根本看不到任何差异,好像 merge 完全是魔术。但是当您运行时:
    git log -- path/to/file1

    还有其他更阴险的事情发生了。这在 the (long) git log documentation中标题为 History Simplification的部分下进行了描述,尽管相当不透明。

    在上面的示例中,假设 git logK向后走。它找到 J,它是一个 merge 。然后,它会检查 IG,在排除了您正在查看的一个文件之外的所有文件之后,将它们与 J进行了比较。也就是说,它只是在三个提交中比较 path/to/file1

    如果 merge 的一侧没有显示对 path/to/file1的任何更改,则意味着 merge 结果 J与输入( IG)没有什么不同。 Git将此称为“TREESAME”。如果 merge 的 J在被简化为一个文件之后,匹配了类似地被剥离的 IG,则 J是treetAME到 IG(或可能是两者)。在这种情况下,Git会选择TREESAME父级或其中的任何一个,并且仅查看该路径。假设它选择的是 I(沿上排),而不是 G(沿下排)。

    在我们的案例中,这意味着如果某人在 merge 过程中丢球,而丢失了原本应该从 J传入 F的更改,则 git log永远不会显示该更改。 log命令先查看 K,然后查看 J,然后查看但删除 G,然后仅查看 I,然后 H,然后 E以及任何更早的提交。它根本不会查看commit F!因此,我们看不到 path/to/file1F的更改。

    这里的逻辑很简单;我将直接引用 git log文档,但要强调一些重点:

    [Default mode] Simplifies the history to the simplest history explaining the final state of the tree. Simplest because it prunes some side branches if the end result is the same ...



    由于删除了 F中的更改,因此Git宣布它们无关紧要。您不需要看他们!我们将完全忽略 merge 的那一面!

    您可以使用 --full-history完全击败它。这告诉Git不要修剪 merge 的任何方面:它应该查看两个历史记录。这将为您找到commit F。添加 -m -p还应该找到所做更改的位置,因为它将找到所有与文件相关的提交:
    git log -m -p --full-history -- path/to/file1

    如果更改在那里(在我们的示例中为 F中提交)并且不再存在,则丢失的方式只有两种:
  • 在普通提交中将其还原(使用git revert或手动进行了还原)。即使没有path/to/file1,您也会将其视为触及历史记录中的-m的提交; -p将显示差异。
  • 或者,由于在 merge 期间被删除而丢失了它们。即使没有-m,您也将看到 merge ,但不确定是否有人将 merge 放到了这里。但-m -p将显示两个父差异,包括应该(但没有)进行更改的差异。
  • 关于Git表示分支已 merge ,但显然不存在更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43927066/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com