git-merge 是否将 "to-be-merged" 分支中的文件添加到头部

Does git-merge add files from the "to-be-merged" branch to head or not

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

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

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

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

编辑:我可能已经找到问题的根源。如果某些任务分支在迭代开始时从 master 分支出来,然后在结束时合并到 test 会怎么样。将 test 合并到 master 时会不会导致一些问题?每当我在 Github 上查看我的 test 分支时,它会显示 "This branch is 7 commits ahead, 5 commits behind master".

git merge合并了testmaster的所有变化,即test中的变化、添加和删除的文件将在[=]中更改、添加或删除13=].

您的步骤是:

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

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

首先,关于 git 操作的行为应该有何不同的直接问题:

Is this how git-merge works? It only updates the changed files in master and doesn't care about added files?

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

So is my only option to do a git-rebase?

除了一些基于边缘情况的例外情况,您应该期望结果内容 (TREE) 对于变基和合并是相同的。无论是什么阻碍了您的合并按预期执行,都可能 导致 rebase 以意外的方式运行。

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

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

很难确切地说 发生了什么,因为很难获得有关回购当前状态的足够信息。但我可以拟定一些场景并谈谈合并的工作原理,也许这会给你足够的继续你可以 trouble-shoot 它...

假设您像这样启动合并:

git checkout branchA
git merge branchB

现在 git 将确定三个提交:

  • 我们的 - 已签出的提交(即 branchA 指向的内容)
  • 他们的 - 历史被合并的提交(即 branchB` 指向的内容)
  • 基础 - 我们的他们的之间的"merge base"。通常这可以总结为 "the most recent common ancestor of ours and theirs"

所以如果你有

A -- B -- C -- D -- O <--(branchA)
      \
       E -- F -- T <--(branchB)

那么O将是我们的T将是他们的B将是 base.

接下来 git 将计算 BT 之间的补丁(粗略的差异) - 我们可以称之为 "their changes" - 以及 [= 之间的补丁31=] 和 O - 我们可以称之为 "our changes".

如果提交 E 添加一个 previously-nonexistent 文件 foo.txt,那么在我们的更改中将不会对该文件进行任何更改(因为它不存在于 [=31] =] 或 O),但 foo.txt 将在他们的更改中成为一个新文件(因为它在 B 中不存在,但它确实存在于 T 中)。

现在 git 想要创建一个包含我们的更改和他们的更改的组合补丁。如果我们的更改和他们的更改都试图修改相同的代码块,这就是冲突可能出现的地方。但在我们的 "new file" 场景中,不会有冲突,因为我们的更改没有提及 foo.txt。合并后的补丁应包含 "new file foo.txt" 以及出现在 T.

处的文件内容

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

git merge-base branchA branchB

将打印出一个提交 ID。问题是,提交是否包含foo.txt?如果是这样,那么您将不会得到您期望的行为;然后问题简化为“弄清楚为什么计算出的合并基数包含 foo.txt.

现在在上面的示例中,此命令将 return B 的 ID,其中不包含 foo.txt。那么,为什么您的情况会有所不同?

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

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 被创建的事实 "from master" 并不重要。事实上,目前 git 中存储的任何内容甚至 都反映了 它是从 master 创建的事实;它也可以在提交 C.

之前从 test 创建

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

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

重新开始
A -- B <--(master)
      \
       C -- D <--(test)

开发人员在 master 上开始编码 task1。他们承诺,而你已经

A -- B -- E <--(master)
      \
       C -- D <--(test)

然后有人说 "hey, dude, you can't just work directly on 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)

看起来不错,但是有人说"hey, we can't have the changes form E on master until test gets merged back; and we can't afford the problems that a history rewrite would cause. So..."

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是可以到达所有其他人/无法被任何o到达的人其他人。

因此以 E 作为合并基础,它们的更改 不会影响 foo.txt(因为它已经在 E 中创建),而 我们的更改 删除了 foo.txt(在 ~E 中)。所以合并结果没有 foo.txt,这不是来自分支的更改——它已经在 master 的历史中发生了更改——所以 PR 报告对此只字不提。

我知道这到底是怎么回事吗?不,我敢打赌它很像这样吗?是的。我可以详细说明修复它的程序吗?不一定,因为可能会涉及到确切的步骤,并取决于完全发生了什么。

现在我会先尝试确认问题是否与我所建议的基本一致。