简体   繁体   English

git merge更新源分支上未更改的文件

[英]git merge updates files not changed on source branch

I'm struggling to understand how the following behavior is a good thing in git. 我很难理解以下行为在git中是一件好事。 See below for an example I put together to help illustrate my problem. 请参阅下面的示例,我将它放在一起以帮助说明我的问题。 Many times my team and myself are getting changes/commits going into branches that we did not want to go there. 很多时候,我的团队和我自己正在进行更改/提交进入我们不想去那里的分支机构。

> 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(+)

Notice in the above, a.txt is updated in the merge, even though it was not touched/modified on branch2. 请注意,在上面,a.txt在合并中更新,即使它未在branch2上触摸/修改。 In the above scenario I would expect git to be intelligent to recognize that a.txt was not changed on branch2 and therefore when applying updates to branch1, not make those changes. 在上面的场景中,我希望git能够智能地识别出branch2上没有更改a.txt,因此在对branch1应用更新时,不要进行这些更改。

Is there something I'm doing wrong? 有什么我做错了吗? Yes, I could cherry pick and that would would for this simplistic example where I know what I changed, but is not realistic under real circumstances where the changes are much larger and you don't know what might have been affected. 是的,我可以选择,这将是这个简单的例子,我知道我改变了什么,但在真正的情况下变化更大,你不知道可能会受到什么影响,这是不现实的。

To be clear, I do not want this behavior from git. 要清楚,我不希望这种行为来自git。

If the commands above are completely and correctly typed, then git is correct. 如果完全正确地输入上面的命令,那么git是正确的。 Here's what you did: 这是你做的:

  1. created a repo (defaults to branch "master") 创建了一个repo(默认为分支“master”)
  2. added one changeset (2 new files) to "master" 添加了一个变更集(2个新文件)到“主”
  3. created a branch ("branch1") but did not change to it 创建了一个分支(“branch1”),但没有改变它
  4. added one changeset (changed a.txt) on "master" 在“主人”上添加了一个变更集(更改了a.txt)
  5. created a branch ("branch2") and changed to it (this branch includes a.txt modified) 创建了一个分支(“branch2”)并更改为它(此分支包含a.txt修改)
  6. added one chageset (changed b.txt) on "branch2" 在“branch2”上添加了一个chageset(更改了b.txt)
  7. switched to "branch1" (contains two original, unchanged files) 切换到“branch1”(包含两个原始的,未更改的文件)
  8. merged (fast-forward) with "branch2" (applied both changes: a.txt and b.txt) 与“branch2”合并(快进)(应用两个更改:a.txt和b.txt)

This is exactly what you describe and exactly what should happen. 这正是您描述的内容以及应该发生的内容。 Where you probably went wrong was thinking you were changing a.txt on "branch1" when you really changed it on "master" before you created "branch2" thus giving the illusion that changes magically appeared on "branch1" from "master" when merging with "branch2" but in reality the change came from "branch2". 在您创建“branch2”之前,如果您真的在“master”上更改了它,那么您可能会错误地认为您正在更改“branch1”上的a.txt,从而在合并时给出了“master1”中“branch1”神奇变化的错觉与“branch2”但实际上变化来自“branch2”。

If you repeat your test but at step 3 switch to "branch1" ( git checkout -b branch1 ) instead of committing changes to a.txt to "master" I think you'll get the merge you expect. 如果你重复测试, 但在第3步切换到“branch1”git checkout -b branch1 ),而不是将更改提交到a.txt到“master”,我想你会得到你期望的合并。

'branch1' and 'branch2' are nothing but commit pointers. 'branch1'和'branch2'只不过是提交指针。 They are states of the commit history at certain moments in time. 它们是某些时刻的提交历史状态。 As such, when merging 'branch2' into 'branch1', git does little more than establish a common ancestor and attempt to apply changes from both trees, together. 因此,当将'branch2'合并到'branch1'时,git只会建立一个共同的祖先,并尝试将两个树中的更改一起应用。

Take a simple diagram: 拿一个简单的图表:

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

In the example above, 'branch1' points at commit B and 'branch2' points at commit E . 在上面的示例中,'branch1'指向提交B ,'branch2'指向提交E This describes, more or less, the order of operations you entered above. 这或多或少地描述了您在上面输入的操作顺序。 Were you to merge 'branch2' into 'branch1', git would find a common ancestor in B then apply all the history that exists between B and E to 'branch1', specifically commits C , D , and E . 如果你将'branch2'合并到'branch1',git会在B找到一个共同的祖先,然后将BE之间存在的所有历史应用于'branch1',特别是提交CDE

What you want, however, is just E . 然而,你想要的只是E One (bad) solution would be cherry-picking, as you've already identified. 正如您已经确定的那样,一个(坏的)解决方案就是采摘樱桃。 A much better solution is rebasing 'branch2' onto 'branch1', thereby rewriting 'branch2's history to include only commit E past 'branch1': 一个更好的解决方案是将'branch2'重新定义到'branch1',从而重写'branch2的历史记录,只包括提交E过去'branch1':

git rebase --onto branch1 master branch2

That results in exactly what you seek, and reads as 'rebase branch2, which was originally based on master, onto branch1'. 这导致了你所寻求的,并且读作'rebase branch2,它最初基于master,到branch1'。 Note, I've left the 'branch1' pointer out of this diagram for simplicity, and E became E' because its commit hash changed (as is a common convention with these diagrams): 注意,为了简单起见,我将'branch1'指针从这个图中删除了, E变成了E'因为它的提交哈希发生了变化(这是这些图的常见约定):

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

You could get a similar effect with git checkout branch2 && git rebase -i B , then remove commits C and D from the interactive rebase session. 您可以使用git checkout branch2 && git rebase -i B获得类似的效果,然后从交互式rebase会话中删除提交CD

At my last job we routinely faced this problem with isolated feature branches. 在我上一份工作中,我们经常使用隔离的功能分支来解决这个问题。 Cut at different moments in time from the same production branch, they would pull along unwanted changes if merged without rebasing. 在同一生产分支的不同时刻切割,如果合并而没有变基,他们会进行不必要的更改。 As an integration manager, I routinely rewrote their histories to a common point in the past (the last production release), thereby allowing clean merges all the way through. 作为一名集成经理,我经常将他们的历史重写为过去的共同点(上一个生产版本),从而允许整个过程中的清晰合并。 It's one of many possible workflows. 这是许多可能的工作流程之一。 The best answer depends heavily on how your team moves code around. 最佳答案在很大程度上取决于您的团队如何移动代码。 In a CI environment, for example, it's sometimes less important that C and D get pulled along with merges like the one you describe. 例如,在CI环境中,将CD与您描述的合并一起拉出来有时不那么重要。

Finally, note that if E depends on any code in C or D , this solution will wreak havoc on your history when merging 'branch1' (now containing the E' change set) back into 'master'. 最后请注意,如果E依赖于CD任何代码,当将“branch1”(现在包含E'更改集)合并回“master”时,此解决方案将对您的历史记录造成严重破坏。 If your workflow is incremental, and 'branch1' and 'branch2' meddle in similar functions and files, merge conflicts will arise as a matter of course. 如果您的工作流是增量的,并且'branch1'和'branch2'插入类似的函数和文件,那么合并冲突将成为理所当然。 In that case, a closer look at your team's workflow is probably warranted. 在这种情况下,可能需要仔细查看您团队的工作流程。

a.txt is updated in the merge, even though it was not touched/modified on branch2 a.txt在合并中更新,即使它未在branch2上触摸/修改

But it was sir, it was. 但它是先生,它是。 Run all command but git merge branch2 , then 然后运行所有命令但git merge branch2

$ cat a.txt
data a

$ git checkout branch2
Switched to branch 'branch2'

$ cat a.txt
data a
more data a

I understand that it is in branch2 that way, however I did not modify/touch it after I branched. 我明白它在branch2中是这样的,但是在我分支之后我没有修改/触摸它。

You committed the more data a to master . 您提交了more data amaster You then created branch2 from master . 然后,您从master创建了branch2 Hence branch2 will contain more data a as well. 因此, branch2也将包含more data a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM