Answers:
这里的观察结果是,在您开始工作之后branch1
(忘记或不意识到先切换到另一个分支会很好branch2
),您可以运行:
git checkout branch2
有时Git会说:“好,您现在在branch2上!” 有时候,吉特说:“我做不到,我会丢失一些更改。”
如果Git 不允许您这样做,则必须提交更改,以将其永久保存在某个地方。 您可能想要使用git stash
保存它们;这是它设计的目的之一。 请注意,git stash save
或git stash push
实际上是指 “提交所有更改,但完全不要分支,然后从我现在的位置删除它们。” 这样就可以切换:您现在没有正在进行的更改。git stash apply
切换后即可使用它们。
边栏:
git stash save
是旧语法;git stash push
是在Git 2.13版中引入的,用于解决自变量的一些问题git stash
并允许使用新选项。当以基本方式使用时,两者的作用相同。
如果Git 不允许您进行切换,则说明您已经有解决方法:请使用git stash
或git commit
;或者,如果您所做的更改很难重新创建,请使用git checkout -f
它来强制执行。这个答案是所有关于何时混帐会让你git checkout branch2
即使你开始做一些改变。为什么它的工作有时,而不是其他时间?
这里的规则以一种方式是简单的,而以另一种方式是复杂的/难以解释的:
也就是说,请注意,这仍然是简化的。有一些极难处理的极端情况,其中包括git add
,git rm
等等branch1
。A 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这要求我们定义文件在分支中的含义,而这又需要正确定义单词branch。(参见我们究竟由“分支”是什么意思?)这里,我真正的意思是提交到分支名称解析:其路径是一个文件是在如果产生了哈希值。如果收到错误消息,则该文件不在其中。回答此特定问题时,索引或工作树中路径的存在无关紧要。因此,这里的秘诀是检查每个P
branch1
git rev-parse branch1:P
branch1
P
git rev-parse
branch-name:path
。这可能由于文件最多位于一个分支而失败,或者给了我们两个哈希ID。如果两个哈希ID 相同,则两个分支中的文件相同。无需更改。如果哈希ID不同,则两个分支中的文件不同,并且必须更改以切换分支。
此处的关键概念是永久冻结提交中的文件。您将编辑的文件显然没有冻结。至少在一开始,我们仅查看两个冻结提交之间的不匹配。 不幸的是,我们-或Git的,还必须处理的文件是不是在提交你打算从开关路程,是在提交你要切换到。这导致了剩下的复杂性,因为文件也可以存在于索引和/或工作树中,而不必存在我们正在使用的这两个特定的冻结提交。
2如果它已经包含“正确的内容”,则可以将其视为“安全排序”,因此Git毕竟不必创建它。我记得至少有一些版本的Git允许这样做,但是现在进行的测试表明,它在Git 1.8.5.4中被视为“不安全”。相同的参数将应用于修改后的文件,该文件恰好被修改为与to-to-switch-to分支匹配。同样,1.8.5.4只是说“将被覆盖”。也请参阅技术说明的末尾:我的内存可能有问题,因为自从我从1.5版本开始使用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
至此,即使我们打开,工作树文件也将inboth
匹配其中的一个。此更改未上演提交,显示如下:branch2
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'
在这里,暂存和工作副本都与in中的匹配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
file,而,工作树文件不同于暂存文件。工作树版本与branch1
(aka 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
,暂存文件也不匹配,所以a git checkout
会丢失该副本,而会git checkout
被拒绝。
所有这些的基本实现机制是Git的index。索引,也称为“临时区域”,是您构建下一个提交的地方:它开始与当前提交匹配,即,无论您现在已经签出什么,然后每次git add
文件时,都替换索引版本无论您在工作树中拥有什么。
记住,工作树是您处理文件的地方。在这里,它们具有常规形式,而不是像在提交和索引中那样特殊的仅对Git有用的形式。因此,您从提交中提取文件,通过索引,然后进入工作树。更改后,将git add
其更改为索引。因此,每个文件实际上有三个位置:当前提交,索引和工作树。
当您运行git checkout branch2
,什么混帐做封面的下面是比较尖承诺的branch2
到无论是在双方最近提交现在的指数。任何与当前文件匹配的文件,Git都可以保留。都没动过。两次提交中相同的任何文件,Git可以可以保留下来-这些文件使您可以切换分支。
由于该索引,很多Git(包括提交切换)相对较快。实际上,索引中的内容不是每个文件本身,而是每个文件的hash。文件本身的副本作为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
,而不是您可能期望的git read-tree
文档中,而是文档中进行了描述。)
您有两种选择:存储更改:
git stash
然后再将它们找回来:
git stash apply
或将更改放在分支上,以便可以获取远程分支,然后将更改合并到该分支上。这是git的最大优点之一:您可以创建一个分支,提交它,然后将其他更改提取到您所在的分支上。
您说这没有任何意义,但是您只是在做,因此您可以在拉动之后随意合并它们。显然,您的另一选择是提交分支的副本,然后执行拉取。假设是您不想这样做(在这种情况下,我很困惑您不想要分支),或者您担心冲突。
git stash pop
,如果成功应用,它将从列表中删除该存储。
git stash pop
,除非您打算在回购记录中
如果新分支包含与该特定已更改文件的当前分支不同的编辑,则在提交或隐藏更改之前,不允许您切换分支。如果两个分支上更改的文件相同(即该文件的提交版本),则可以自由切换。
例:
$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "adding file.txt"
$ git checkout -b experiment
$ echo 'goodbye world' >> file.txt
$ git add file.txt
$ git commit -m "added text"
# experiment now contains changes that master doesn't have
# any future changes to this file will keep you from changing branches
# until the changes are stashed or committed
$ echo "and we're back" >> file.txt # making additional changes
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
file.txt
Please, commit your changes or stash them before you can switch branches.
Aborting
这适用于未跟踪的文件以及跟踪的文件。这是一个未跟踪文件的示例。
例:
$ git checkout -b experimental # creates new branch 'experimental'
$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "added file.txt"
$ git checkout master # master does not have file.txt
$ echo 'goodbye world' > file.txt
$ git checkout experimental
error: The following untracked working tree files would be overwritten by checkout:
file.txt
Please move or remove them before you can switch branches.
Aborting
一个很好的例子说明了为什么要在进行更改的同时在分支之间移动,如果您正在对master进行一些实验,想要提交它们,但还不想掌握...
$ echo 'experimental change' >> file.txt # change to existing tracked file
# I want to save these, but not on master
$ git checkout -b experiment
M file.txt
Switched to branch 'experiment'
$ git add file.txt
$ git commit -m "possible modification for file.txt"
git checkout -m
,它将您的工作树和索引更改合并到新的结帐中。