gpt4 book ai didi

Git应该显示没有 merge 冲突

转载 作者:行者123 更新时间:2023-12-02 22:46:20 30 4
gpt4 key购买 nike

根据我对 merge 冲突的理解,当两个人更改了同一文件和/或修改了该文件中的同一行时,就会发生 merge 冲突。所以当我做一个
git pull origin master
我期望 merge 冲突,因为两个版本中的同一行是不同的,但是看起来git决定覆盖我的本地文件。

要提供更多信息,
几天前,我在Github上推送了我的版本。然后有人 pull 了它,使用它,然后将其推回到github。另一个家伙修改的两个文件对我来说很有趣。

第一个文件是配置文件,另一个人在其中更改了密码。因此,当我从github pull 出时,本地版本中的密码与github上的密码不同。但是,在我的终端上,它说

Auto-merging <filename>

而且,它覆盖了我的文件,密码是另一个人设置的密码。

感兴趣的第二个文件是用模板引擎(PUG)编写的HTML文件。另一个人更改了该文件中的许多内容,例如添加了许多CSS类,删除了我曾经使用过的某些类,添加了指向CSS文件的链接以及所有其他内容。但是,当我 pull 出它时,终端甚至没有提到它正在自动 merge ,只是改写了我本地存储库中的整个文件,并使用了Github中的文件。

对于这两个文件,我的问题是,这是使用git pull的预期行为,还是我做错了什么?

以下是我使用的命令。
git checkout -b "misc"
git pull origin master

另外,我尝试只使用fetch,然后手动 merge/提交,但是当我使用fetch时,什么也没发生。文件完全没有更改。

我以前使用过git/github,但从未真正在使用分支和从github进行推/pull 的团队中进行广泛的工作。

最佳答案

Git的行为正确。这是预期的结果(尽管对您而言不是真正的“期望”)。
关于如何与Git一起使用以使其对您真正有用的内容,在底部有一些介绍。
除了Mykhailo Kovalskyi's answer之外,还有一种更可能的情况。您这样做:

git checkout -b "misc"
git pull origin master
第一行很简单。这是第二个问题,因为 git pullgit fetch,后面是 git merge,这两者都非常复杂。
绘制图形(请参见 Pretty git branch graphs)
每当您在Git中使用分支时-并且您始终在使用分支时,所以这实际上只是“当您使用Git时”-重要的是要牢记提交图。该图或DAG(有向无环图)始终存在,通常潜伏在视线之外。要使用 git log进行查看,请使用 --graph(通常与 --oneline一起使用)。要使用可视化工具查看它,请使用诸如 gitk之类的东西或许多烦人的GUI中的一种,这些 View 会给您提供类似于 here所示的 View (这是关于stackoverflow的随机选择的问题,它涉及 gitkgit-gui中看到的内容)。
该图确定了 merge 的工作方式,因此在当时非常重要。在其他时间,它通常只是潜伏在路,但始终存在。 Git中的几乎所有内容都围绕添加提交,这会向该图添加条目。1
因此,让我们画一些图,然后观察 git fetchgit merge的运行情况。
这是一个只有 master分支的存储库图,上面有四个提交:
o--o--o--o   <-- master
master分支“指向”最尖端的提交。在此图中,右侧有较新的提交,即最右边的提交。
每个提交也指向其父提交。也就是说, o--o--o中的线实际上应该是箭头: o <- o <- o。但是这些箭头都指向后方,这很烦人,对人类几乎没有用,因此最好将它们绘制为线条。事实是,这些向后箭头是Git查找较早提交的方式,因为分支名称仅指向最尖端的提交!
Git还具有名称 HEAD,它是“当前提交”的符号。 HEAD正常工作的方式是它实际上包含分支名称,然后该分支名称指向提示提交。我们可以用一个单独的箭头绘制它:
                  HEAD
|
v
o--o--o--o <-- master
但这占用了太多空间,因此我通常使用以下方法:
o--o--o--o   <-- master (HEAD)
Git将发现 HEAD已“附加到” master(包含名称) master,然后按照从 git checkout -b misc到向后提交的反向箭头进行操作。

Hint: use git log --decorate to show branch names and HEAD. It's particularly good with --oneline --graph: think of this as a friendly dog: Decorate, Oneline, Graph. In Git 2.1 and later, --decorate happens automatically, so you don't have to turn it on yourself most of the time. See also this answer to Pretty git branch graphs.

Note that git log --decorate prints the decoration as HEAD -> master when HEAD points to master. When HEAD points directly to a commit, Git calls this a detached HEAD, and you might see HEAD, master instead. This formatting trick was new in Git 2.4: before that, it just showed HEAD, master for both detached HEAD mode, and non-detached-HEAD mode, for this case. In any case, I call "non-detached" an attached HEAD, and I think master (HEAD) shows this attachment pretty well.)


现在, git gc步骤创建一个新的分支名称。默认情况下,这个新的分支名称指向当前的(HEAD)提交,所以现在我们有了:
o--o--o--o   <-- master, misc (HEAD)

1实际上,您永远都不能更改提交。看起来可以改变提交的事情,实际上是通过添加类似于旧提交的新提交来起作用的,然后它们掩盖了旧提交,而是向您展示了新提交。这使提交看起来像已更改,但没有更改。您也不能删除(或至少不能直接删除)提交:您只能从分支和标记名等中使它们不可访问。一旦提交不可到达,Git的维护“垃圾收集器”最终将其删除。现在使 git fetch删除它们可能很困难。即使您希望它们消失,Git也会尽力让您找回。
但是,所有这些仅适用于提交,因此是经验法则:“提早且经常提交”。您实际提交的任何内容,Git都会尝试让您稍后再检索,通常最多30或90天。
git fetch git fetch origin的功能可以概括为:
  • 调用另一个Git;
  • 询问它具有哪个提交;和
  • 收集这些提交,以及使这些提交变得明智的所有其他要求,然后将它们添加到您的存储库中。

  • 这样,Git就像 The Borg。但是,而不是:“我们是博格人。我们将把您的生物学和技术独特性添加到我们自己中,” git 说:“我就是 git 。您的技术独特的 promise 将被添加到我自己中!”
    因此,让我们看看 master时会发生什么。你有这个:
    o--o--o--o   <-- master, misc (HEAD)
    他们有这个,在 origin/master上有一些额外的提交(而且我们现在不在乎他们的HEAD):
    o--o--o--o--o--o   <-- master
    您的Git重命名了它们的主文件,并在自己的末端将其称为 origin,以便您可以使它们保持笔直。他们的两个新提交都添加到您的存储库中,所有这些都类似于Borg。这些新的提交指向具有常规向后箭头的现有四个提交,但是现在绘制图形需要更多的空间:
    o--o--o--o     <-- master, misc (HEAD)
    \
    o--o <-- origin/master
    请注意,您的所有分支都没有更改。仅 origin/master更改。您的Git增加了它们的技术独特性2,并重新指向 master以跟踪“上次我检查时 origin12ab9fc7...上的位置”。

    2这是那些大而丑陋的SHA-1 ID进入的地方。散列是Git如何分辨哪些提交对于哪个存储库是唯一的。关键是同一提交始终具有相同的哈希ID,因此,如果其Git具有commit 12ab9fc7...,而您的Git具有commit git merge,则您的Git已经具有其提交,反之亦然。所有这些背后的数学是相当深刻而优美的。
    git pull git merge的第二部分是运行 git merge origin/master。它运行 git merge的等效项3。 git merge命令从查找 merge 基础开始,这是图突然变得很重要的地方。
    松散地说,两次提交之间的 merge 基础是“图中所有线重新回到一起的点”。通常,两个提交是两个分支提示,由两个分支名称指向。一个典型的且很明显的情况是:
               o--o      <-- branch1 (HEAD)
    /
    o--o--o--*
    \
    o--o--o <-- branch2
    *的作用是找到最近的共同祖先提交,我将其绘制为 o而不是这里的 git merge。这就是 merge 的基础。这只是两个分支“ fork ”的起点。 branch1的目标是找出“您”已更改的内容-自提交 *以来您在 branch2中所做的工作-以及“他们”已更改的内容,即自提交 *以来 git diff中已更改的内容。为了获得这些更改,Git运行两个 *命令。
    即使我们这样绘制提交,也是如此:
    o--o--o--*--o--o     <-- branch1 (HEAD)
    \
    o--o--o <-- branch2
    这是同一张图,所以它是同一张 merge 。 Git将提交 branch1*的提示进行比较(“我们的两次提交有什么变化?”),然后将 branch2*的提示进行提交(“其三个提交有什么变化?”)。然后,Git会尽力 merge 这些更改,并根据结果进行新的 merge 提交。所有这些 merge 和提交的确切细节都无关紧要,因为我们没有这样的图。
    我们拥有的是:
    o--o--o--*        <-- master, misc (HEAD)
    \
    o--o <-- origin/master
    请注意,我在这里保留了 git merge的概念。那是因为 misc仍然找到 merge 基础。这里的问题是 merge 基础是分支技巧:名称 *直接指向commit git diff <commit-*> <commit-*>
    如果Git做 *,则差异显然为空。 Commit *与commit misc相同。那么如何 merge 这些呢?
    Git的答案是:我们根本不会 merge 。我们做到了Git所说的快速前进。请注意,尽管内部提交箭头都向后指向,但是如果我们仅想象它们朝前指向,则现在可以轻松获取 HEAD分支标签并将其向前滑动,沿着狗腿向下然后向右滑动。结果看起来像这样:
    o--o--o--o        <-- master
    \
    o--o <-- origin/master, misc (HEAD)
    所以现在我们的配置文件是 misc提交中的一个,这是 origin/master的最尖端的提交,与 git merge origin/master相同的提交。
    换句话说,我们丢失了对配置文件的更改,因为它们被对配置文件的更改所覆盖。

    3为何实际上不使用 git fetch origin的细节在这里几乎无关紧要,但与历史有很大关系。在Git的早期版本1.8.4之前,实际上没有真正更新过 origin/master的某些 git fetch。这是一个错误的设计决定,在所有现代Git版本中, master都会对其进行更新。

    会进行“真正的 merge ”而不是快速的帮助吗?
    如果我们回到原始设置(并删除名字 git pull,因为它已经打乱了):
    o--o--o--*        <-- misc (HEAD)
    \
    o--o <-- origin/master
    我们可以代替让 git merge运行 git merge --no-ff origin/master,而运行我们自己的 git pull,以 merge origin/master,但不允许Git进行快速转发。这样有帮助吗?
    las,不。请记住, merge 的目的是 merge 自 merge 基础以来的所有更改。因此,Git将运行两个差异:
    git diff <commit-*> <commit-*>     # this diff is empty
    git diff <commit-*> origin/master # this is "what they changed"
    然后,Git将我们的更改(无)与它们的更改 merge ,并进行新的 merge 提交:
    o--o--o--o------o   <-- misc (HEAD)
    \ /
    o--o <-- origin/master
    我们有一个不同的图(有点像汤勺或 Big Dipper),但是我们接受了它们的更改,包括密码更改,而没有保留任何内容(自 merge 基础以来我们没有任何更改)。
    使 merge 变得有用
    我们需要确保“我们的”更改(在Git的眼中,这一定是我们的更改)“看起来与”他们的更改“不同”。这意味着我们需要Git选择其他 merge 基础。
    正如我上面所说, merge 基础是我们的提交及其提交开始分歧的点。这意味着我们需要创建自己的分支,并确保我们不要“快速前进”,甚至根本不要“快速前进”。
    因此,我们可能确实希望避免使用 A .4。我们也可能希望选择一个较早的时间来建立自己的分支。我们希望图的分支保持它们自身的独特性。我给了其中一些提交字母名称,以便我可以谈论它们:
         A-----B      <-- misc (HEAD)
    / /
    o--o--o--o <-- master
    \
    o--C <-- origin/master
    在commit git merge中,我们将配置文件更改为具有不同的密码。然后,我们 master(不是快进) master的技巧来获取新内容,而无需更改密码。这个步骤可能是非常手动的,也可能是全自动的,但是一旦提交,我们就完成了:提交是永久的;它们无法更改。5
    现在,我们可以像往常一样允许 git merge origin/master“快进”:
         A-----B      <-- misc (HEAD)
    / /
    o--o--o--*--o--C <-- master, origin/master
    现在,当我们 git merge master* 6时, merge 基础将是我标记为 *的提交。如果我们没有将密码从 B更改为 *,而他们将其密码从 C更改为 A,我们将接听他们的更改-但他们不再需要更改它,因为我们从不发送密码来提交 B*;我们将这些保留给我们自己。因此,密码不应从 C更改为 master,并且在进行新 merge 时将保留更改后的密码:
         A-----B-----D   <-- misc (HEAD)
    / / /
    o--o--o--o--o--C <-- master, origin/master
    稍后,我们将拾取更多提交,将它们 merge (快进)到 C,并准备再次 merge :
         A-----B-----D   <-- misc (HEAD)
    / / /
    o--o--o--o--o--C--o--o <-- master, origin/master
    这次, merge 基础将是 misc提交-这是 C及其分支上最接近的提交-Git将diff origin/masterD进行比较。大概他们仍然不会更改密码,因为我们仍然没有给他们提交 git pull

    4我尽可能避免使用 master,但是根据您的操作方式,无论如何您都可以使用它,尤其是对于 git commit --amend
    5我们通过将分支标签移至新提交来进行任何普通的新提交:请记住,分支名称仅指向最尖端的提交。我们只是进行了一个新的提示提交,其父项是之前的最提示提交,然后重新指向标签,向前迈进了一步。但是看看当我们进行新的提交时,会发生什么呢?它的父项不仅指向旧的提示提交,还指向更远的地方。现在,我们通过 stash 一些先前的提交来“重写历史记录”。 (尝试绘制此图。) git rebasemaster都是这样工作的。
    6请注意,这些命令执行相同的操作,因为 origin/master的尖端和 master的尖端是相同的提交。一个区别是默认提交消息将更改:一个将说“merge 主文件”,而另一个将说“merge 原件/主文件”。 (Git的提交消息格式中有一些奇怪的东西,也将 .config与其他所有东西都区别对待,但是我们可以忽略不计。这只是一个历史产物。)

    最后一点:提交中的配置和密码=错误
    由于提交是如此永久性,因此将密码放入其中通常是一个非常糟糕的主意。有权访问您的存储库的任何人都可以浏览历史提交并找到密码。
    通常,也不应该完全提交配置文件,尽管这里没有真正的安全问题。相反,这是您遇到的一个非常问题的问题:每个人都需要不同的配置。将您的数据库提交到共享存储库没有任何意义。如果它是一个私有(private)存储库,那会更有意义,如果它是一个私有(private)分支,那就可以了(如果在大多数情况下仍然不是最优的)。
    想要某种示例配置或默认的初始配置是很常见的。这些确实应该在提交中。诀窍是要确保样本或默认的初始配置与“实时”配置分开。例如,对于某些系统,您将包括:
    config.default
    并有一些代码,例如:
    [ -f .config ] || cp config.default .config
    在第一次运行时将默认配置设置为 .config文件。然后,使用 .gitignore中的ojit_code,它将永远不会放入存储库中,因此它将永远不会出现在任何提交中,并且您首先不会遇到这个问题。

    关于Git应该显示没有 merge 冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40097125/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com