7

我正在努力理解以下行为如何在 git 中是一件好事。请参阅下面的示例,以帮助说明我的问题。很多时候,我的团队和我自己都在将更改/提交进入我们不想去那里的分支。

> git init sandbox && cd sandbox
> echo "data a" > a.txt && echo "data b" > b.txt
> git add -A && git commit -a -m "initial population"
[master (root-commit) d7eb6af] initial population
 2 files changed, 2 insertions(+)
 create mode 100644 a.txt
 create mode 100644 b.txt
> git branch branch1
> echo "more data a" >> a.txt && git commit -a -m "changed a.txt on master"
[master 11eb82a] changed a.txt on master
 1 file changed, 1 insertion(+)
> git branch branch2 && git checkout branch2
Switched to branch 'branch2'
> echo "more data b" >> b.txt && git commit -a -m "changed b.txt on branch2"
[branch2 25b38db] changed b.txt on branch2
 1 file changed, 1 insertion(+)
> git checkout branch1
Switched to branch 'branch1'
> git merge branch2
Updating d7eb6af..25b38db
Fast-forward
 a.txt | 1 +
 b.txt | 1 +
 2 files changed, 2 insertions(+)

请注意,在上面,a.txt 在合并中更新,即使它在 branch2 上没有被触及/修改。在上述场景中,我希望 git 能够智能地识别出分支 2 上的 a.txt 没有更改,因此在将更新应用于分支 1 时,不要进行这些更改。

有什么我做错了吗?是的,我可以挑选,对于这个简单的例子,我知道我做了什么,但在实际情况下是不现实的,因为变化要大得多,你不知道可能会受到什么影响。

需要明确的是,我不希望 git 出现这种行为。

4

3 回答 3

3

如果上面的命令输入完整且正确,那么 git 是正确的。这是你所做的:

  1. 创建了一个仓库(默认为分支“master”)
  2. 向“master”添加了一个变更集(2 个新文件)
  3. 创建了一个分支(“branch1”)但没有改变它
  4. 在“master”上添加了一个变更集(更改了 a.txt)
  5. 创建了一个分支(“branch2”)并更改为它(此分支包含修改的 a.txt)
  6. 在“branch2”上添加了一个 chageset(更改为 b.txt)
  7. 切换到“branch1”(包含两个未更改的原始文件)
  8. 与“branch2”合并(快进)(应用了两个更改:a.txt 和 b.txt)

这正是您所描述的,也正是应该发生的事情。您可能出错的地方是认为您正在更改“branch1”上的 a.txt,而实际上您在创建“branch2”之前在“master”上更改了它,从而产生了在合并时从“master”神奇地出现在“branch1”上的错觉使用“branch2”,但实际上变化来自“branch2”。

如果您重复您的测试,但在第 3 步切换到“branch1”git checkout -b branch1)而不是将 a.txt 的更改提交到“master”,我认为您会得到您期望的合并。

于 2013-01-22T04:06:54.703 回答
3

'branch1' 和 'branch2' 只不过是提交指针。它们是特定时刻提交历史的状态。因此,当将“branch2”合并到“branch1”时,git 所做的只是建立一个共同的祖先并尝试将两棵树的更改一起应用。

来一张简单的图:

 branch1       E <- branch2
    |         /
    v        /
A - B - C - D <- master

在上面的例子中,'branch1' 指向 commitB和 'branch2' 指向 commit E。这或多或少地描述了您在上面输入的操作顺序。如果您将 'branch2' 合并到 'branch1' 中,git 会在其中找到一个共同的祖先,B然后将存在于B和之间的所有历史记录应用E到 'branch1',特别是提交CDE

然而,你想要的只是E. 正如您已经确定的那样,一种(不好的)解决方案是挑选樱桃。一个更好的解决方案是将 'branch2' 重新定位到 'branch1' 上,从而重写 'branch2' 的历史记录以仅包含E过去 'branch1' 的提交:

git rebase --onto branch1 master branch2

这正是您所寻求的结果,并读作“将最初基于 master 的分支 2 重新设置为分支 1”。E请注意,为简单起见,我将 'branch1' 指针排除在此图中,E'因为它的提交哈希已更改(这些图的常见约定):

       E' <- branch2
      /
     /
A - B - C - D <- master

您可以使用 获得类似的效果,然后从交互式 rebase 会话中git checkout branch2 && git rebase -i B删除提交。CD

在我的上一份工作中,我们经常遇到孤立的功能分支的问题。在不同的时间从同一个生产分支中剪切,如果在没有变基的情况下合并,它们会拉动不需要的变化。作为一名集成经理,我经常将他们的历史重写到过去的一个共同点(最后一个生产版本),从而允许从头到尾进行干净的合并。这是许多可能的工作流程之一。最佳答案很大程度上取决于您的团队如何移动代码。例如,在 CI 环境中,有时并不那么重要,C并且D像您描述的那样与合并一起被拉动。

最后,请注意,如果E依赖于Cor中的任何代码,当将“branch1”(现在包含更改集)D合并回“master”时,此解决方案将对您的历史记录造成严重破坏。E'如果您的工作流程是增量的,并且“branch1”和“branch2”涉及类似的功能和文件,那么合并冲突自然会出现。在这种情况下,可能需要仔细查看您团队的工作流程。

于 2013-01-22T04:37:51.280 回答
2

a.txt 在合并中更新,即使它没有在 branch2 上被触及/修改

但它是先生,它是。运行所有命令但git merge branch2, 然后

$ cat a.txt
data a

$ git checkout branch2
Switched to branch 'branch2'

$ cat a.txt
data a
more data a

我知道它在 branch2 中,但是在我分支后我没有修改/触摸它。

你承诺more data a. master然后,您branch2master. 因此branch2也将包含more data a

于 2013-01-22T04:06:33.160 回答