Git 合并影响工作目录和暂存区 [英] Git merge affects on working directory and staging area

查看:45
本文介绍了Git 合并影响工作目录和暂存区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Git 的新手,并试图了解 git merge 命令的工作原理.所以我尝试了一个简单的 git 项目,例如:

I am new by Git and trying to know how git merge command works. So I tried a simple git project for example:

我有两个分支:masterb.您可以在此处查看两者的 git log(1,2,4 是在分支中创建的示例文件):

I have two branches: master and b. You can see git log for both here (1,2,4 are sample files that are created in branches):

* cf3456b (HEAD, b) add and modify 4 in b
* 68b9086 edit 1 in branch b
| * 81e6490 (master) remove 1 from branch b1
| * e0a6844 modify 2 in branch b1
| * 06bad1d add2 in branch b1
|/  
* c667d3b add 1 in branch master

所以
master 只有一个文件:2
b 只有两个文件:1,4

So
master only has one file: 2
b only has two files: 1,4

现在当我尝试做:

git merge master

我看到了这条消息:

CONFLICT (modify/delete): 1 在 master 中删除并在 HEAD 中修改.1 的版本 HEAD 留在树中.自动合并失败;修复冲突然后提交结果.

CONFLICT (modify/delete): 1 deleted in master and modified in HEAD. Version HEAD of 1 left in tree. Automatic merge failed; fix conflicts and then commit the result.

这对我来说很奇怪,我之前想过:

That is strange for me, I thought before that:

  1. merge 就像 checkout 尝试将内容从 merge in 分支的最新提交复制到 merge into 分支但与 checkout 相反,merge 尝试不更改 merge into 中的任何文件.最后,如果两个 merge inmerge into 分支具有相同的文件,但状态不同,发生冲突.
  2. 我知道 git 只保存文件的 snapshot 而不是 differences.那么 git 如何知道分支的最新提交的变化呢?(在message中可以看到1在master中被删除,在HEAD中被修改是不同的)
  1. merge like a checkout tries to copy content from latest commit of merge in branch to latest commit of merge into branch but opposite of checkout, merge tries to do not change any file in merge into.And finally if both merge in and merge into branches have same file with different status, conflict occurs.
  2. I know that git only saves snapshot of files instead of differences. So how git know about changes in latest commits of branches? (In message you can see 1 deleted in master and modified in HEAD which are differences)

现在,如果我的想法是正确的,文件 1 应该不会发生冲突.所以我的想法是不正确的,但是 merge 究竟是如何工作的?

Now if my thought was true, no conflict should occur for file 1. So my thought is not true, but how merge works really?

推荐答案

Re point #2,你说 git 保存快照是正确的.但是,给定任意两个快照 ab,我们可以通过比较 ba 来获得变更集(差异)>—git 会根据需要做到这一点.

Re point #2, you are correct that git saves snapshots. However, given any two snapshots a and b, we can get a changeset (difference) by comparing b vs a—and git does just that, as needed.

至于合并本身,首先,我们必须注意到 git 保留了一个提交图(也称为DAG"或提交 DAG",因为该图是一个 D引发A循环Graph或DAG).这是您的 git log --graph 输出显示的内容,尽管在这种特殊情况下,我们有一个非常简单的图形形式,它只是一棵树(直到提交合并).

As for the merge itself, first, we must note that git keeps a commit graph (also called "the DAG" or "the commit DAG", as the graph is a Directed Acyclic Graph or DAG). This is what your git log --graph output shows, though in this particular case we have such a simple form of graph that it's just a tree (until the merge is committed).

对于任何树,并且有许多 DAG,1 给定树中的两个节点,我们可以找到唯一的 Lowest Common A祖先节点,或 LCA.这里的两个节点是两个分支的tips——commits cf3456b(你当前分支的tips)和81e6490(master的tips))——在这种特殊情况下,LCA 是第一次提交,c667d3b.在像这样的简单情况下,LCA 很容易在视觉上发现:您只需查看提交图即可找到两个分支连接的位置(从那里到根的所有提交都在 bothem> 分支).

With any tree, and with many DAGs,1 given two nodes in the tree we can find a unique Lowest Common Ancestor node, or LCA. The two nodes here are the tips of the two branches—commits cf3456b (the tip of your current branch) and 81e6490 (the tip of master)—and the LCA in this particular case is the first commit, c667d3b. In simple cases like this, the LCA is easy to spot visually: you just look through the commit graph to find the place that the two branches join up (all commits from there, back to the root, are on both branches).

这个 LCA 节点是合并基础.Git 首先找到当前提交的合并基础和你给它的参数.(对于八爪鱼合并",您指示 git 将多个提交合并到当前分支上,这项工作涉及更多一点,但我们可以在这里忽略这些.)

This LCA node is the merge base. Git first finds the merge base of the current commit and the argument you give it. (For an "octopus merge", where you direct git to merge multiple commits onto the current branch, the job is a little more involved, but we can just ignore those here.)

接下来,给定现有的合并基础和两个不同的提示提交,git 必须计算两个变更集:一个从合并基础到当前提交,一个从合并基础到参数提交.请注意,git 会在整个过程中预先执行一次此操作,之后它可以继续执行合并操作.

Next, given the existing merge base and the two distinct tip commits, git must compute two changesets: one from the merge-base to the current commit, and one from the merge-base to the argument commit. Note that git does this once, up front, for the entire process, after which it can proceed with the merge action.

现在,对于每个有更改的文件,git 必须合并这些更改.对于大多数简单的修改,方法很简单:如果文件 1 只在一个分支中被修改,则按原样进行修改.如果它在两个分支中都被修改,请尝试合并修改,只取一份副本,其中两个分支都进行了相同的更改.当然,如果两个分支对单个文件的同一区域进行了不同的更改,就会发生冲突.

Now, for each file for which there are changes, git must combine the changes. For most simple modifications, the method is straightforward: if file 1 is modified in just one branch, take the modification as is. If it's modified in both branches, attempt to combine the modifications, taking just one copy where both branches made the same change. Of course, you get a conflict if both branches made differing changes to the same region of a single file.

对于文件创建或删除情况,或者当有重命名时,事情变得有点棘手.如果一个文件在一个分支中被删除而在另一个分支中没有被触及,git 可以通过删除文件来解决这个问题(如果它已经在 HEAD 中被删除,则保持删除",如果它在另一个中被删除,则删除它犯罪).如果文件在一个分支中被重命名,并在另一个分支中被修改,git 也可以组合这些更改(在导入或保留其他更改的同时执行或保留重命名).然而,对于其他任何事情,git 只是简单地声明一个冲突,举起它的比喻手,然后让你解决冲突.

For file creation or deletion cases, or when there are renames, things get a bit trickier. If a file is deleted in one branch and untouched in the other, git can resolve this by deleting the file ("keeping it deleted" if it's already deleted in HEAD, deleting it if it's deleted in the other commit). If the file is renamed in one branch, and modified in the other, git can combine these changes as well (doing or keeping the rename while also importing or retaining the other changes). For just about anything else, though, git simply declares a conflict, throws up its metaphorical hands, and makes you resolve the conflict.

在这种情况下,文件1确实在您当前的分支中被修改,而在master中确实被删除了.Git 不确定是删除文件(按照合并基到 master diff 的指示),还是保留更改(按照合并基到 HEAD 的指示)diff),所以它会给你留下文件和冲突.

In this case, file 1 really was modified in your current branch, and really was deleted in master. Git is not sure whether to delete the file (as directed by the merge-base to master diff), or keep the changes (as directed by the merge-base to HEAD diff), so it leaves you with the file and a conflict.

如果你在两个分支中都创建了一个新文件 5,git 会再次给你一个冲突(除非,如果文件的新内容在两个分支中是相同的——我没有测试这种情况).

If you had created a new file 5 in both branches, git would again give you a conflict (except, possibly, if the file's new contents are the same in both branches—I have not tested this case).

1对于复杂的 DAG,可能有多个最低公共祖先候选者.对于 git 的递归"合并,git 通过合并每个 LCA 候选者以形成新的虚拟基础"来处理此问题.这个虚拟基础成为比较两个提交的合并基础.如果只有两个 LCA 候选,则通过粗略等价获得虚拟合并基础:

1With complex DAGs there may be multiple Lowest Common Ancestor candidates. For git's "recursive" merge, git handles this by merging each LCA candidate to form a new "virtual base". This virtual base becomes the merge-base against which the two commits are compared. If there are only two LCA candidates, the virtual merge base is obtained by doing the rough equivalent of:

git checkout -b temp candidate_1
git merge candidate_2
git commit -a -m ""

在此内部合并"期间发生的任何合并冲突都将被忽略:冲突的合并基础与它的冲突一起使用.这可能会导致一些看起来很奇怪的冲突,尤其是在外部"合并的同一区域存在冲突时:请参阅 this SO question.

Any merge conflicts that occur during this "inner merge" are ignored: the conflicted merge-base is used with its conflicts in place. This can make for some funky-looking conflicts, especially when there's a conflict in the same region of the "outer" merge: see this SO question.

如果有两个以上的 LCA 候选,merge-recursive 取前两个并合并它们,然后合并第三个,然后合并第四个,以此类推.即使它可以合并对以将数字减半,然后合并合并对以再次将其减半,依此类推,它也会这样做.也就是说,给定 N 个 LCA 候选者,有可能进行 ceil(log2(N)) 合并,而不是 N-1 次合并,但 git 不会——无论如何 N 很少超过 2.

If there are more than two LCA candidates, merge-recursive takes the first two and merges them, then merges the third, then merges the fourth, and so on, iteratively. It does this even though it could merge pairs to cut the number in half, then merge the merged-pairs to cut it in half again, and so on. That is, given N LCA candidates, it would be possible to do ceil(log2(N)) merges, rather than N-1 merges, but git doesn't—N rarely exceeds 2 anyway.

这篇关于Git 合并影响工作目录和暂存区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆