gpt4 book ai didi

git - 当前分支上有未提交的更改时 checkout 另一个分支

转载 作者:行者123 更新时间:2023-12-01 16:52:42 25 4
gpt4 key购买 nike

大多数情况下,当我尝试 check out 另一个现有分支时,如果我在当前分支上有一些未提交的更改,Git 不允许我这样做。所以我必须首先提交或 stash 这些更改。

但是,有时 Git 确实允许我在不提交或 stash 这些更改的情况下 check out 另一个分支,并且它会将这些更改传送到我 check out 的分支。

这里的规则是什么?更改是暂存的还是未暂存的有关系吗?将更改带到另一个分支对我来说没有任何意义,为什么 git 有时会允许它?也就是说,它在某些情况下有用吗?

最佳答案

初步说明
这个答案试图解释 Git 的行为方式。不建议参与任何特定的工作流程。 (我自己的偏好是无论如何都提交,避免 git stash 并且不要试图太棘手,但其他人喜欢其他方法。)
这里的观察是,在您开始工作后 branch1 (忘记或没有意识到首先切换到不同的分支 branch2 会很好),您运行:

git checkout branch2
有时 Git 会说“好吧,你现在在 branch2 上!”有时,Git 会说“我不能那样做,我会丢失你的一些更改。”
如果 Git 不允许你这样做,你必须提交你的更改,将它们永久保存在某个地方。 You may want to use git stash to save them; this is one of the things it's designed for.请注意 git stash savegit stash push实际上的意思是“提交所有更改,但根本不在任何分支上,然后将它们从我现在所在的位置删除。”这使得切换成为可能:您现在没有进行中的更改。然后您可以 git stash apply切换后的它们。

Sidebar: git stash save is the old syntax; git stash push was introduced in Git version 2.13, to fix up some problems with the arguments to git stash and allow for new options. Both do the same thing, when used in the basic ways.


如果你愿意,你可以在这里停止阅读!
如果 Git 不让你切换,你已经有了补救措施:使用 git stashgit commit ;或者,如果您的更改很容易重新创建,请使用 git checkout -f强制它。这个答案是关于 Git 何时让你开放 git checkout branch2即使你开始做出一些改变。为什么它有时起作用,而有时不起作用?
这里的规则一方面很简单,另一方面很复杂/难以解释:
当且仅当所述切换不需要破坏这些更改时,您才可以在工作树中切换带有未提交更改的分支。
那就是——请注意,这仍然是简化的;有一些额外困难的角落案例,带有分阶段 git add s, git rm s 等等——假设你在 branch1 .一个 git checkout branch2必须这样做:
  • 对于 branch1 中的每个文件而不是在 branch2 ,1 删除该文件。
  • 对于 branch2 中的每个文件而不是在 branch1 ,创建该文件(具有适当的内容)。
  • 对于两个分支中的每个文件,如果 branch2 中的版本不同,更新工作树版本。

  • 这些步骤中的每一个都可能会破坏您的工作树中的某些内容:
  • 如果工作树中的版本与 branch1 中提交的版本相同,则删除文件是“安全的” ;如果您进行了更改,则它是“不安全的”。
  • 以文件显示的方式创建文件 branch2如果现在不存在,则为“安全”。2 如果现在确实存在但内容“错误”,则为“不安全”。
  • 当然,如果工作树版本已经提交到 branch1,用不同版本替换文件的工作树版本是“安全的”。 .

  • 创建新分支 ( git checkout -b newbranch ) 始终被认为是“安全的”:作为此过程的一部分,不会在工作树中添加、删除或更改任何文件,并且索引/暂存区也不会受到影响。 (警告:在不更改新分支起点的情况下创建新分支是安全的;但是如果您添加另一个参数,例如 git checkout -b newbranch different-start-point ,这可能需要更改内容,以移动到 different-start-point 。然后 Git 将应用像往常一样结帐安全规则。)

    1这要求我们定义文件在分支中的含义,这反过来又需要正确定义分支这个词。 (另见 What exactly do we mean by "branch"? )在这里,我真正的意思是分支名称解析到的提交:路径为 P 的文件。 branch1如果 git rev-parse branch1:P产生一个散列。那个文件 不是 branch1如果您收到错误消息。路径的存在 P在回答这个特定问题时,您的索引或工作树中的内容无关紧要。因此,这里的 secret 是检查 git rev-parse 的结果。在每个 branch-name:path .这要么因为文件“在”最多一个分支而失败,要么给了我们两个哈希 ID。如果两个哈希 ID 相同,则两个分支中的文件相同。无需更改。如果hash ID不同,说明两个分支的文件不同,必须改成切换分支。
    这里的关键概念是提交中的文件永远卡住。您将编辑的文件显然没有被卡住。至少在最初,我们只关注两个卡住提交之间的不匹配。 不幸的是,我们(或 Git)还必须处理不在您要切换的提交中但在您要切换到的提交中的文件。这导致了剩余的复杂性,因为文件也可以存在于索引和/或工作树中,而不必存在我们正在处理的这两个特定的卡住提交。
    2如果它已经存在“正确的内容”,那么它可能被认为是“安全的”,这样 Git 毕竟不必创建它。我记得至少有一些 Git 版本允许这样做,但刚刚的测试表明它在 Git 1.8.5.4 中被认为是“不安全的”。相同的参数适用于修改后的文件,该文件恰好被修改以匹配 to-be-switch-to 分支。同样,1.8.5.4 只是说“将被覆盖”。另请参阅技术说明的结尾:我的内存可能有问题,因为我认为自从我在 1.5.something 版本中第一次开始使用 Git 以来,读取树规则没有改变。

    更改是暂存的还是未暂存的有关系吗?
    是的,在某些方面。特别是,您可以暂存更改,然后“取消修改”工作树文件。这是两个分支中的文件,在 branch1 中有所不同和 branch2 :
    $ git show branch1:inboth
    this file is in both branches
    $ git show branch2:inboth
    this file is in both branches
    but it has more stuff in branch2 now
    $ git checkout branch1
    Switched to branch 'branch1'
    $ echo 'but it has more stuff in branch2 now' >> inboth
    此时,工作树文件 inbothbranch2 中的匹配,即使我们在 branch1 .此更改不是为提交而暂存的,这就是 git status --short显示在这里:
    $ git status --short
    M inboth
    space-then-M 表示“已修改但未暂存”(或更准确地说,工作树副本与暂存/索引副本不同)。
    $ git checkout branch2
    error: Your local changes ...
    好的,现在让我们暂存工作树副本,我们已经知道它也与 branch2 中的副本匹配.
    $ git add inboth
    $ git status --short
    M inboth
    $ git checkout branch2
    Switched to branch 'branch2'
    这里的暂存和工作副本都与 branch2 中的内容相匹配。 ,因此允许结帐。
    让我们尝试另一个步骤:
    $ git checkout branch1
    Switched to branch 'branch1'
    $ cat inboth
    this file is in both branches
    我所做的更改现在从暂存区丢失了(因为结帐是通过暂存区写入的)。这是一个角落案例。变化没有消失,但我上演的事实已经消失了。
    让我们暂存文件的第三个变体,与任一分支副本不同,然后设置工作副本以匹配当前分支版本:
    $ echo 'staged version different from all' > inboth
    $ git add inboth
    $ git show branch1:inboth > inboth
    $ git status --short
    MM inboth
    两人 M s 这里的意思是:暂存文件不同于 HEAD文件,并且,工作树文件与暂存文件不同。工作树版本确实匹配 branch1 (又名 HEAD)版本:
    $ git diff HEAD
    $
    但是 git checkout不允许结帐:
    $ git checkout branch2
    error: Your local changes ...
    让我们设置 branch2版本作为工作版本:
    $ git show branch2:inboth > inboth
    $ git status --short
    MM inboth
    $ git diff HEAD
    diff --git a/inboth b/inboth
    index ecb07f7..aee20fb 100644
    --- a/inboth
    +++ b/inboth
    @@ -1 +1,2 @@
    this file is in both branches
    +but it has more stuff in branch2 now
    $ git diff branch2 -- inboth
    $ git checkout branch2
    error: Your local changes ...
    即使当前的工作副本与 branch2 中的匹配,暂存文件没有,所以 git checkout会丢失那个副本,而 git checkout被拒绝。
    技术说明 - 仅适用于非常好奇的 :-)
    所有这一切的底层实现机制是 Git 的索引。索引,也称为“暂存区”,是您构建下一次提交的地方:它开始匹配当前提交,即您现在 check out 的任何内容,然后每次您 git add一个文件,您可以用您的工作树中的任何内容替换索引版本。
    请记住,工作树是您处理文件的地方。在这里,它们有它们的正常形式,而不是像在提交和索引中那样的一些特殊的仅对 Git 有用的形式。所以你从提交中提取一个文件,通过索引,然后进入工作树。更改后,您 git add它到索引。所以实际上每个文件都有三个位置:当前提交、索引和工作树。
    当您运行时 git checkout branch2 ,Git 在幕后所做的是比较 branch2 的提示提交到当前提交和索引中的任何内容。任何与现有文件匹配的文件,Git 都可以不理会。这一切都没有受到影响。任何在两次提交中都相同的文件,Git 也可以单独保留——这些是让您切换分支的文件。
    由于这个索引,Git 的大部分,包括提交切换,都相对较快。索引中实际包含的不是每个文件本身,而是每个文件的哈希值。文件本身的副本作为 Git 称为 blob 对象的内容存储在存储库中。这也类似于文件在提交中的存储方式:提交实际上并不包含文件,它们只是将 Git 引导到每个文件的哈希 ID。因此,Git 可以比较哈希 ID(目前为 160 位长的字符串)来决定提交 X 和 Y 是否具有相同的文件。然后它也可以将这些哈希 ID 与索引中的哈希 ID 进行比较。
    这就是导致上述所有古怪极端情况的原因。我们有提交 X 和 Y,它们都有文件 path/to/name.txt ,我们有 path/to/name.txt 的索引条目.也许所有三个哈希都匹配。也许其中两个匹配,一个不匹配。可能三个人都不一样吧。而且,我们可能还有 another/file.txt仅在 X 中或仅在 Y 中,并且现在是否在索引中。这些不同的情况中的每一种都需要单独考虑:Git 是否需要将文件从提交复制到索引,或从索引中删除,以从 X 切换到 Y?如果是这样,它还必须将文件复制到工作树,或将其从工作树中删除。如果是这种情况,索引和工作树版本最好至少匹配一个已提交的版本;否则 Git 会破坏一些数据。
    (所有这些的完整规则都在,而不是您可能期望的 git checkout 文档中,而是在 the git read-tree documentation, under the section titled "Two Tree Merge" 中描述。)

    关于git - 当前分支上有未提交的更改时 checkout 另一个分支,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22053757/

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