git-merge 是否会将文件从 "to-be-merged" 分支添加到 head 中



所以我和我的团队正在敏捷开发流程下做一个项目。每次迭代test分支都有几个分支。每个分支充当迭代的任务。迭代完成后,所有任务分支将合并到test分支中,test分支将合并到master中。

我遇到的问题是将test合并到master中。在这种特定情况下,master相当落后,test有许多文件甚至没有包含在master.因此,当我通过拉取请求比较testmaster时,它就像test中的所有新文件都不会添加到master中一样。

这就是 git 合并的工作方式吗?它只更新master中更改的文件,而不关心添加的文件?

那么我唯一的选择是做一个 git-rebase 吗?如果是这样,它是如何工作的?

编辑:我可能已经找到了问题的根源。如果某些任务分支在迭代开始时从master分支出来,然后在迭代结束时合并到test中,该怎么办?将test合并到master时会导致一些问题吗?每当我在 Github 上查看我的test分支时,它都会说:"这个分支领先 7 次提交,在 master 后面 5 次提交"。

根据您的编辑,让我们将其分为两部分。

首先,您直接询问不同的 git 操作应该如何表现:

这就是 git 合并的工作方式吗?它只更新 master 中更改的文件,而不关心添加的文件?

否;远程分支上的任何更改(包括添加新文件)都应通过合并合并。 如果这没有发生,那么其他事情正在发生。

那么我唯一的选择是做一个 git-rebase 吗?

除了基于边缘情况的几个例外情况,您应该期望生成的内容(TREE)对于变基和合并相同。 无论是什么原因导致您的合并无法按预期执行,可能导致变基以意想不到的方式运行。

变基或合并的选择(理论上)仅与如何保存历史有关。

第二:好的......那么这里到底发生了什么?

很难确切地说出发生了什么,因为很难获得有关存储库当前状态的足够信息。 但是我可以制定一些场景并讨论一下合并的工作原理,也许这会给你足够的时间继续下去,你可以对它进行故障排除......

假设您启动了如下所示的合并:

git checkout branchA
git merge branchB

现在git将确定三个提交:

  • 我们的- 已签出的提交(即branchA指向的内容)
  • 他们的- 其历史记录被合并的提交(即分支 B' 指向的内容)
  • 基地-我们他们之间的"合并基地"。 通常,这可以概括为">我们他们最近的共同祖先">

所以如果你有

A -- B -- C -- D -- O <--(branchA)

E -- F -- T <--(branchB)

然后O将是我们的T将是他们的B将是基础

接下来 git 将计算BT之间的补丁(大致是差异)——我们可以称之为"他们的更改"——以及BO之间的补丁——我们可以称之为"我们的更改"。

如果 commitE添加了以前不存在的文件foo.txt,那么在我们的更改中该文件不会有任何更改(因为它在BO中都不存在),但foo.txt在他们的更改中将是一个新文件(因为它在B中不存在,但在T中确实存在)。

现在 git 想要创建一个包含我们的更改及其更改的组合补丁。 如果我们的更改及其更改都尝试修改同一堆代码,这就是冲突可能出现的地方。 但是在我们的"新文件"场景中,不会有冲突,因为我们的更改没有说明foo.txt。 组合的修补程序应包括"新文件foo.txt",其中包含文件的内容,如T所示。

所以大多数时候,如果merge的行为令人困惑,你可以通过知道什么被用作合并基础来开始理解它。

git merge-base branchA branchB

将打印出提交 ID。 问题是,提交是否包含foo.txt? 如果是这样,那么你就不会得到你期望的行为;然后将问题简化为"找出为什么计算的合并基础包含foo.txt.

现在在上面的例子中,这个命令将返回B的 ID,它不包含foo.txt。 那么,为什么在你的情况下会有所不同呢?

在编辑中,您描述了某些分支是从 master 创建的并合并到测试中的。 这不一定是问题。 假设我们有

A -- B <--(master)

你说

git checkout master
git branch test

然后在test上进行一些工作。

A -- B <--(master)

C -- D <--(test)

现在有人创建了一个任务分支,但他们从master创建它.

A -- B <--(master)
|
| E -- F <--(task1)

C -- D <--(test)

然后他们task合并为test

A -- B <--(master)
|
| E ----- F <--(task1)
         
C -- D -- T <--(test)

其他一些工作继续影响master

A -- B -- G -- H -- O <--(master)
|
| E ----- F <--(task1)
         
C -- D -- T <--(test)

现在在这种情况下,task1是"从master"创建的事实并不重要。 事实上,目前存储在git中的任何内容都没有反映它是从master创建的事实;它也可以在提交之前从test创建C.

这意味着合并仍然可以正常工作。

但这是一个不同的场景... 如果从master创建task1的人在意识到他们需要分支之前实际上将更改提交到master怎么办? 所以在这种情况下,我们从

A -- B <--(master)

C -- D <--(test)

开发人员开始在master上编写task1。 他们承诺,你有

A -- B -- E <--(master)

C -- D <--(test)

然后有人说"嘿,伙计,你不能直接在master上工作",所以我们试图解决问题。

git checkout master
git checkout -b task1
# work continues on the task1 branch
git commit

还有一些其他工作发生在master上,所以现在你有

A -- B -- E -- G <--(master)
    
    F <--(task1)

C -- D <--(test)

这看起来不错,但有人说"嘿,在test合并回来之前,我们无法将更改形式Emaster;我们承受不起重写历史会导致的问题。 所以...">

git checkout master
git revert HEAD^

我们有

A -- B -- E -- G -- ~E <--(master)
    
    F <--(task1)

C -- D <--(test)

每个分支上的所有内容看起来都正确,因此工作仍在继续

A -- B ------- E -- G -- ~E -- H -- O <--(master)
         
         F <--(task1)
         
C -- D -- T <--(test)

但是现在当你尝试将test合并到master中时,发生了一些可怕的事情:git认为合并基础是E的,因为mastertest之间的共同祖先,E是可以到达所有其他/任何其他人都无法到达的。

因此,使用E作为合并基础,它们的更改不会影响foo.txt(因为它已经在E中创建),而我们的更改删除了foo.txt(~E年)。 所以合并结果没有foo.txt,这不是分支的变化——这是master历史上已经有的变化——所以公关报告没有提到它。

我知道这到底是怎么回事吗? 不。 我敢打赌它几乎是这样的吗? 是的。 我可以拼出一个修复它的过程吗? 不是真的,因为可能涉及确切的步骤,并且取决于确切发生的事情。

现在,我将首先尝试确认问题是否从根本上是我所建议的。

git merge包含从testmaster的所有更改,即test中更改,添加和删除的文件将在master中更改,添加或删除。

您的步骤是:

git checkout master
git merge test 
// if required: fix merge conflicts, shouldn't be required if `test` was in `sync` with master at the start of your sprint
git commit // if fixing merge conflicts were necessary
git push

在理想情况下,当mastertest在冲刺开始时同步并且所有内容都可以合并回来时,这两个分支将在冲刺结束时再次同步。您的工作流程类似于 gitflow - 只是您将分支称为test而不是develop

我的建议:当多人在存储库(分支)上工作时,如果可能的话,避免变基。这是根据风险重写的历史。

此外,github,TFS或VSTS等源代码管理工具正在合并而不是变基。

另请参阅文档 https://git-scm.com/docs/git-merge

最新更新