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

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

问题描述

我是新的 Git 并试图知道 git merge 命令是如何工作的。所以我尝试了一个简单的git项目,例如:

我有两个分支: master b'/ code>。你可以在这里看到git log( 1,2,4 是在分支中创建的示例文件):

  * cf3456b(HEAD,b)在b 
中添加和修改4 * 68b9086在分支中编辑1 b
| * 81e6490(master)从分支b1
|中删除1 * e0a6844在分支b1
|中修改2 * 06bad1d分支中的add2 b1
| /
* c667d3b分支大师中加1



<所以

master 只有一个文件: 2


b 只有两个文件: 1 4



现在当我尝试去做:

  git merge master 

我看到以下信息:


CONFLICT(修改/删除):1在master中删除并在HEAD中修改。
版本树中剩余1个HEAD。自动合并失败;修复冲突
然后提交结果。

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


$ b $ ol
  • 合并类似于结帐尝试将最新的合并内容从分支中的内容复制到的最新提交,并入分支但与 checkout merge 尝试不改变合并到中的任何文件。最后如果合并到并且合并到分支具有不同状态的相同文件,则会发生冲突。

  • 我知道 git 只会保存快照的文件,而不是差异。那么git如何知道最新的分支提交的变化? (在消息中,您可以看到 1在master中被删除并且在HEAD 中被修改,这是不同之处)

    现在,如果我的想法是真的,那么文件 1 应该不会发生冲突。所以我的想法是不正确的,但 merge 真的有效吗?

    解决方案

    重点#2,你是正确的,git保存快照。然而,给定任意两个快照 a b ,我们可以通过比较<$ c $获得变更集c> b vs a - 并且git根据需要做到这一点。



    至于合并本身,首先,我们必须注意到,git保留了一个提交图(也称为DAG或提交DAG,因为图是 D 定向 A 循环 g raph或DAG)。这就是你的 git log --graph 输出显示的内容,尽管在这种情况下我们有这样一种简单的图表形式,它只是一个树(直到合并被提交) 。

    使用任何树和多个DAG,给定树中的两个节点,我们可以找到一个唯一的 L strong> owest C ommon A ncestor节点或 LCA 。这里的两个节点是两个分支的提示 - 提交 cf3456b (您当前分支的提示)和 81e6490 master )的提示),并且此特定情况下的LCA是第一次提交,即 c667d3b 。在这种简单的情况下,LCA很容易在视觉上发现:您只需查看提交图来查找两个分支联合的位置(从那里返回到根的所有提交都在 branches)。



    此LCA节点是合并基础。 Git首先找到当前提交和您提供的参数的合并基础。 (对于一个章鱼合并,你可以直接将git合并到当前分支的多个提交中,这个工作稍微牵扯一点,但我们可以在这里忽略它们。)



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



    现在,对于每个文件发生更改,git必须结合这些变化。对于大多数简单的修改,该方法非常简单:如果文件 1 仅在一个分支中修改,请按原样进行修改。如果它在两个分支中都被修改,请尝试合并修改,只需两个分支进行相同更改即可获得一个副本。当然,如果两个分支对单个文件的同一区域进行了不同的更改,则会发生冲突。



    对于文件创建或删除情况,或者存在重命名时,事情变得有点棘手。如果一个文件在一个分支中被删除并且在另一个分支中未被触发,git可以通过删除该文件来解决这个问题(如果已经在 HEAD 中删除​​了该文件,则删除它如果它在另一个提交中被删除)。如果文件在一个分支中被重命名并且在另一个分支中被修改,那么git也可以结合这些更改(做或保留重命名,同时还导入或保留其他更改)。然而,对于其他任何事情,git只是声明一个冲突,抛出它的隐喻手,并且让你解决冲突。



    在这种情况下,文件 1 确实已在您当前的分支中修改过,而且确实已在 master 中删除​​。 Git不确定是否删除文件(按照merge-base指示 master diff),或者保留更改(按照merge-base指示 HEAD diff),所以它会留下文件和冲突。



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




    1 对于复杂的DAG,可能会有多个最低共同祖先候选项。对于git的递归合并,git通过合并每个LCA候选人来形成一个新的虚拟基础来处理这个问题。这个虚拟基础成为两个提交进行比较的合并基础。如果只有两个LCA候选者,虚拟合并基础可以通过大致等价的方式获得:

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

    任何在此内部合并期间发生的合并冲突将被忽略:冲突的合并基础将与其冲突一起使用。这可能会造成一些时髦的冲突,特别是当外部合并的同一区域存在冲突时:请参阅这个SO问题。如果有两个以上的LCA候选人, merge-recursive 取前两个合并它们,然后合并第三个,然后迭代地合并第四个,依此类推。它可以做到这一点,即使它可以合并成对数字,然后合并合并对以再次将其切成两半,依此类推。也就是说,如果有N个LCA候选者,则可以执行ceil(log2(N))合并,而不是N-1合并,但git doesn't-N很少会超过2个。

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

    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
    

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

    Now when I try to do:

    git merge master
    

    I saw this message:

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

    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, 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.

    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).

    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).

    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.)

    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.

    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.

    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.

    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.

    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).


    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 ""
    

    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.

    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天全站免登陆