当前位置:   article > 正文

当前分支上有未提交的更改时签出另一个分支_uncommitted changes before checkout

uncommitted changes before checkout

本文翻译自:Checkout another branch when there are uncommitted changes on the current branch

Most of the time when I try to checkout another existing branch, Git doesn't allow me if I have some uncommitted changes on the current branch. 在大多数情况下,当我尝试签出另一个现有分支时,如果我在当前分支上有一些未提交的更改,Git不允许我这样做。 So I'll have to commit or stash those changes first. 因此,我必须首先提交或存储这些更改。

However, occasionally Git does allow me to checkout another branch without committing or stashing those changes, and it will carry those changes to the branch I checkout. 但是,偶尔Git确实允许我签出另一个分支而不提交或存储那些更改,并且它将那些更改带到我签出的分支中。

What is the rule here? 这是什么规则? Does it matter whether the changes are staged or unstaged? 变更是分阶段还是分阶段是否重要? Carrying the changes to another branch doesn't make any sense to me, why does git allow it sometimes? 对我来说,将更改转移到另一个分支毫无意义,为什么git有时会允许它? That is, is it helpful in some situations? 也就是说,在某些情况下是否有帮助?


#1楼

参考:https://stackoom.com/question/1UXBl/当前分支上有未提交的更改时签出另一个分支


#2楼

You have two choices: stash your changes: 您有两种选择:存储更改:

git stash

then later to get them back: 然后再将它们找回来:

git stash apply

or put your changes on a branch so you can get the remote branch and then merge your changes onto it. 或将更改放在分支上,以便可以获取远程分支,然后将更改合并到该分支上。 That's one of the greatest things about git: you can make a branch, commit to it, then fetch other changes on to the branch you were on. 这是git的最大优点之一:您可以创建一个分支,提交它,然后将其他更改提取到您所在的分支上。

You say it doesn't make any sense, but you are only doing it so you can merge them at will after doing the pull. 您说这没有任何意义,但是您只是在做,因此您可以在拉动之后随意合并它们。 Obviously your other choice is to commit on your copy of the branch and then do the pull. 显然,您的另一选择是提交分支的副本,然后执行拉取。 The presumption is you either don't want to do that (in which case I am puzzled that you don't want a branch) or you are afraid of conflicts. 假设是您不想这样做(在这种情况下,我很困惑您不想要分支),或者您担心冲突。


#3楼

If the new branch contains edits that are different from the current branch for that particular changed file, then it will not allow you to switch branches until the change is committed or stashed. 如果新分支包含与该特定已更改文件的当前分支不同的编辑,则在提交或隐藏更改之前,不允许您切换分支。 If the changed file is the same on both branches (that is, the committed version of that file), then you can switch freely. 如果两个分支上更改的文件相同(即该文件的提交版本),则可以自由切换。

Example: 例:

  1. $ echo 'hello world' > file.txt
  2. $ git add file.txt
  3. $ git commit -m "adding file.txt"
  4. $ git checkout -b experiment
  5. $ echo 'goodbye world' >> file.txt
  6. $ git add file.txt
  7. $ git commit -m "added text"
  8. # experiment now contains changes that master doesn't have
  9. # any future changes to this file will keep you from changing branches
  10. # until the changes are stashed or committed
  11. $ echo "and we're back" >> file.txt # making additional changes
  12. $ git checkout master
  13. error: Your local changes to the following files would be overwritten by checkout:
  14. file.txt
  15. Please, commit your changes or stash them before you can switch branches.
  16. Aborting

This goes for untracked files as well as tracked files. 这适用于未跟踪的文件以及跟踪的文件。 Here's an example for an untracked file. 这是一个未跟踪文件的示例。

Example: 例:

  1. $ git checkout -b experimental # creates new branch 'experimental'
  2. $ echo 'hello world' > file.txt
  3. $ git add file.txt
  4. $ git commit -m "added file.txt"
  5. $ git checkout master # master does not have file.txt
  6. $ echo 'goodbye world' > file.txt
  7. $ git checkout experimental
  8. error: The following untracked working tree files would be overwritten by checkout:
  9. file.txt
  10. Please move or remove them before you can switch branches.
  11. Aborting

A good example of why you WOULD want to move between branches while making changes would be if you were performing some experiments on master, wanted to commit them, but not to master just yet... 一个很好的例子说明了为什么要在进行更改的同时在分支之间移动,如果您正在对master进行一些实验,想要提交它们,但还不想掌握...

  1. $ echo 'experimental change' >> file.txt # change to existing tracked file
  2. # I want to save these, but not on master
  3. $ git checkout -b experiment
  4. M file.txt
  5. Switched to branch 'experiment'
  6. $ git add file.txt
  7. $ git commit -m "possible modification for file.txt"

#4楼

Preliminary notes 初步说明

The observation here is that, after you start working in branch1 (forgetting or not realizing that it would be good to switch to a different branch branch2 first), you run: 这里的观察结果是,您开始在branch1工作(忘记或不意识到先切换到其他分支branch2会很好),然后运行:

git checkout branch2

Sometimes Git says "OK, you're on branch2 now!" 有时Git会说:“好,您现在在branch2上!” Sometimes, Git says "I can't do that, I'd lose some of your changes." 有时,吉特说:“我做不到,我会丢失一些更改。”

If Git won't let you do it, you have to commit your changes, to save them somewhere permanent. 如果Git 不允许您这样做,则必须提交更改,以将其永久保存在某个地方。 You may want to use git stash to save them; 您可能想使用git stash保存它们; this is one of the things it's designed for. 这是它设计的目的之一。 Note that git stash save or git stash push actually means "Commit all the changes, but on no branch at all, then remove them from where I am now." 请注意, git stash savegit stash push实际上意味着 “提交所有更改,但完全不要分支,然后从我现在的位置删除它们。” That makes it possible to switch: you now have no in-progress changes. 这样就可以切换:您现在没有正在进行的更改。 You can then git stash apply them after switching. 然后,您可以在切换后git stash apply它们。

Sidebar: git stash save is the old syntax; 边栏: git stash save是旧语法; 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. git stash push在Git 2.13版本中引入,以解决git stash参数的一些问题并允许使用新选项。 Both do the same thing, when used in the basic ways. 当以基本方式使用时,两者的作用相同。

You can stop reading here, if you like! 如果愿意,您可以在这里停止阅读!

If Git won't let you switch, you already have a remedy: use git stash or git commit ; 如果Git 不允许您进行切换,则您已经有一种解决方法:请使用git stashgit commit or, if your changes are trivial to re-create, use git checkout -f to force it. 或者,如果您所做的更改很难重新创建,请使用git checkout -f强制执行。 This answer is all about when Git will let you git checkout branch2 even though you started making some changes. 这个答案是所有关于Git的时候就会让你git checkout branch2即使你开始做一些改变。 Why does it work sometimes , and not other times? 为什么它有时工作,而不是其他时间?

The rule here is simple in one way, and complicated/hard-to-explain in another: 这里的规则以一种方式是简单的,而以另一种方式是复杂的/难以解释的:

You may switch branches with uncommitted changes in the work-tree if and only if said switching does not require clobbering those changes. 当且仅当所述切换不需要破坏那些变更时,才可以切换工作树中未提交的变更的分支。

That is—and please note that this is still simplified; 也就是说,请注意,这仍然是简化的。 there are some extra-difficult corner cases with staged git add s, git rm s and such—suppose you are on branch1 . 有一些极难处理的branch1情况,它们带有git add s, git rm s等,假设您在branch1 A git checkout branch2 would have to do this: git checkout branch2必须这样做:

  • For every file that is in branch1 and not in branch2 , 1 remove that file. 对于每一个文件,该文件branch1不是branch2 ,1删除该文件。
  • For every file that is in branch2 and not in branch1 , create that file (with appropriate contents). 对于每一个文件,该文件branch2不是branch1 ,创建该文件(在适当的内容)。
  • For every file that is in both branches, if the version in branch2 is different, update the working tree version. 对于两个分支中的每个文件,如果branch2的版本不同,请更新工作树版本。

Each of these steps could clobber something in your work-tree: 这些步骤中的每一个都可能破坏您的工作树中的某些内容:

  • Removing a file is "safe" if the version in the work-tree is the same as the committed version in branch1 ; 如果工作树中的版本与branch1提交的版本相同,则删除文件是“安全的”。 it's "unsafe" if you've made changes. 如果您进行了更改,则“不安全”。
  • Creating a file the way it appears in branch2 is "safe" if it does not exist now. 如果文件现在不存在,则按照在branch2出现的方式创建文件是“安全的”。 2 It's "unsafe" if it does exist now but has the "wrong" contents. 2如果它确实存在但内容为“错误”,则为“不安全”。
  • And of course, replacing the work-tree version of a file with a different version is "safe" if the work-tree version is already committed to branch1 . 当然,如果工作树版本已提交给branch1 ,则用其他版本替换文件的工作树版本是“安全的”。

Creating a new branch ( git checkout -b newbranch ) is always considered "safe": no files will be added, removed, or altered in the work-tree as part of this process, and the index/staging-area is also untouched. 创建新分支( git checkout -b newbranch始终被认为是“安全的”:在此过程中,不会在工作树中添加,删除或更改任何文件,并且索引/暂存区也保持不变。 (Caveat: it's safe when creating a new branch without changing the new branch's starting-point; but if you add another argument, eg, git checkout -b newbranch different-start-point , this might have to change things, to move to different-start-point . Git will then apply the checkout safety rules as usual.) (警告:它不改变新的分支的出发点创建一个新的分支时是安全的,但如果你添加另一种说法,例如, git checkout -b newbranch different-start-point ,这可能需要改变的东西,移动到different-start-point 。然后Git将照常应用结帐安全规则。)


1 This requires that we define what it means for a file to be in a branch, which in turn requires defining the word branch properly. 1这要求我们定义文件在分支中的含义,进而需要正确定义单词branch (See also What exactly do we mean by "branch"? ) Here, what I really mean is the commit to which the branch-name resolves: a file whose path is P is in branch1 if git rev-parse branch1: P produces a hash. (参见我们究竟由“分支”是什么意思? )这里,我真正的意思是提交到分支名称解析:其路径是一个文件P branch1如果git rev-parse branch1: P产生哈希。 That file is not in branch1 if you get an error message instead. 如果收到错误消息,则该文件不在 branch1 The existence of path P in your index or work-tree is not relevant when answering this particular question. 回答此特定问题时,索引或工作树中路径P的存在无关紧要。 Thus, the secret here is to examine the result of git rev-parse on each branch-name : path . 因此,这里的秘密是检查每个branch-name : path上的git rev-parse的结果。 This either fails because the file is "in" at most one branch, or gives us two hash IDs. 这可能由于文件最多位于一个分支中而失败,或者给了我们两个哈希ID。 If the two hash IDs are the same , the file is the same in both branches. 如果两个哈希ID 相同 ,则两个分支中的文件相同。 No changing is required. 无需更改。 If the hash IDs differ, the file is different in the two branches, and must be changed to switch branches. 如果哈希ID不同,则两个分支中的文件不同,并且必须更改以切换分支。

The key notion here is that files in commits are frozen forever. 此处的关键概念是 永久冻结提交中的 文件 Files you will edit are obviously not frozen. 您将编辑的文件显然没有冻结。 We are, at least initially, looking only at the mismatches between two frozen commits. 至少在一开始,我们仅查看两个冻结提交之间的不匹配。 Unfortunately, we—or Git—also have to deal with files that aren't in the commit you're going to switch away from and are in the commit you're going to switch to. 不幸的是,我们-或Git的,还必须处理不在提交你打算从切换出来, 在提交你要切换到文件。 This leads to the remaining complications, since files can also exist in the index and/or in the work-tree, without having to exist these two particular frozen commits we're working with. 这导致了剩下的复杂性,因为文件也可以存在于索引和/或工作树中,而不必存在我们正在使用的这两个特定的冻结提交。

2 It might be considered "sort-of-safe" if it already exists with the "right contents", so that Git does not have to create it after all. 2如果它已经包含“正确的内容”,则可以将其视为“安全排序”,因此Git毕竟不必创建它。 I recall at least some versions of Git allowing this, but testing just now shows it to be considered "unsafe" in Git 1.8.5.4. 我记得至少有一些版本的Git允许这样做,但是现在进行的测试表明,它在Git 1.8.5.4中被视为“不安全”。 The same argument would apply to a modified file that happens to be modified to match the to-be-switch-to branch. 相同的参数将应用于修改后的文件,该文件恰好被修改为与to-to-switch-to分支匹配。 Again, 1.8.5.4 just says "would be overwritten", though. 同样,1.8.5.4只是说“将被覆盖”。 See the end of the technical notes as well: my memory may be faulty as I don't think the read-tree rules have changed since I first started using Git at version 1.5.something. 也请参阅技术说明的末尾:我的内存可能有问题,因为自从我从1.5版开始使用Git以来,我不认为读取树规则已更改。


Does it matter whether the changes are staged or unstaged? 变更是分阶段还是分阶段是否重要?

Yes, in some ways. 是的,在某些方面。 In particular, you can stage a change, then "de-modify" the work tree file. 特别是,您可以进行更改,然后“取消修改”工作树文件。 Here's a file in two branches, that's different in branch1 and branch2 : 这是两个分支中的文件,在branch1branch2中是不同的:

  1. $ git show branch1:inboth
  2. this file is in both branches
  3. $ git show branch2:inboth
  4. this file is in both branches
  5. but it has more stuff in branch2 now
  6. $ git checkout branch1
  7. Switched to branch 'branch1'
  8. $ echo 'but it has more stuff in branch2 now' >> inboth

At this point, the working tree file inboth matches the one in branch2 , even though we're on branch1 . 在这一点上,工作树文件inboth相匹配的一个在branch2 ,即使我们是在branch1 This change is not staged for commit, which is what git status --short shows here: 此更改未上演提交,这是git status --short在此处显示的内容:

  1. $ git status --short
  2. M inboth

The space-then-M means "modified but not staged" (or more precisely, working-tree copy differs from staged/index copy). space-then-M表示“已修改但未分段”(或更确切地说,工作树副本与分段/索引副本不同)。

  1. $ git checkout branch2
  2. error: Your local changes ...

OK, now let's stage the working-tree copy, which we already know also matches the copy in branch2 . 好的,现在让我们branch2工作树副本,我们已经知道它也与branch2中的副本匹配。

  1. $ git add inboth
  2. $ git status --short
  3. M inboth
  4. $ git checkout branch2
  5. Switched to branch 'branch2'

Here the staged-and-working copies both matched what was in branch2 , so the checkout was allowed. 在这里,暂存和工作副本都与branch2副本匹配,因此允许签出。

Let's try another step: 让我们尝试另一步:

  1. $ git checkout branch1
  2. Switched to branch 'branch1'
  3. $ cat inboth
  4. this file is in both branches

The change I made is lost from the staging area now (because checkout writes through the staging area). 我所做的更改现在从登台区域丢失(因为结帐会通过登台区域进行写入)。 This is a bit of a corner case. 这有点极端。 The change is not gone, but the fact that I had staged it, is gone. 这种变化是不是走了,但我已经上演它的事实, 已经一去不复返了。

Let's stage a third variant of the file, different from either branch-copy, then set the working copy to match the current branch version: 让我们暂存文件的第三个变体,与任何一个分支副本不同,然后将工作副本设置为匹配当前的分支版本:

  1. $ echo 'staged version different from all' > inboth
  2. $ git add inboth
  3. $ git show branch1:inboth > inboth
  4. $ git status --short
  5. MM inboth

The two M s here mean: staged file differs from HEAD file, and , working-tree file differs from staged file. 这里的两个M表示:暂存文件与HEAD文件不同, 以及 ,工作树文件与暂存文件不同。 The working-tree version does match the branch1 (aka HEAD ) version: 工作树版本确实与branch1 (aka HEAD )版本匹配:

  1. $ git diff HEAD
  2. $

But git checkout won't allow the checkout: 但是git checkout不允许签出:

  1. $ git checkout branch2
  2. error: Your local changes ...

Let's set the branch2 version as the working version: 让我们将branch2版本设置为工作版本:

  1. $ git show branch2:inboth > inboth
  2. $ git status --short
  3. MM inboth
  4. $ git diff HEAD
  5. diff --git a/inboth b/inboth
  6. index ecb07f7..aee20fb 100644
  7. --- a/inboth
  8. +++ b/inboth
  9. @@ -1 +1,2 @@
  10. this file is in both branches
  11. +but it has more stuff in branch2 now
  12. $ git diff branch2 -- inboth
  13. $ git checkout branch2
  14. error: Your local changes ...

Even though the current working copy matches the one in branch2 , the staged file does not, so a git checkout would lose that copy, and the git checkout is rejected. 即使当前工作副本与branch2副本匹配,暂存文件也不匹配,所以git checkout将丢失该副本,并且git checkout被拒绝。

Technical notes—only for the insanely curious :-) 技术说明-仅适用于非常好奇的人:-)

The underlying implementation mechanism for all of this is Git's index . 所有这些的基本实现机制是Git的index The index, also called the "staging area", is where you build the next commit: it starts out matching the current commit, ie, whatever you have checked-out now, and then each time you git add a file, you replace the index version with whatever you have in your work-tree. 索引,也称为“暂存区”,是您构建下一个提交的地方:它开始与当前提交匹配,即,无论您现在已经签出了什么,然后每次git add一个文件时,都替换该索引。索引版本,以及工作树中的所有内容。

Remember, the work-tree is where you work on your files. 记住, 工作树是您处理文件的地方。 Here, they have their normal form, rather than some special only-useful-to-Git form like they do in commits and in the index. 在这里,它们具有常规形式,而不是像在提交和索引中那样特殊的仅对Git有用的形式。 So you extract a file from a commit, through the index, and then on into the work-tree. 因此,您可以通过索引提交中提取文件,然后进入工作树。 After changing it, you git add it to the index. 更改后,将它git add到索引中。 So there are in fact three places for each file: the current commit, the index, and the work-tree. 因此,每个文件实际上有三个位置:当前提交,索引和工作树。

When you run git checkout branch2 , what Git does underneath the covers is to compare the tip commit of branch2 to whatever is in both the current commit and the index now. 当您运行git checkout branch2 ,Git在git checkout branch2所做的就是将branch2尖端提交与当前提交和索引中的内容进行比较。 Any file that matches what's there now, Git can leave alone. 任何与当前文件匹配的文件,Git都可以保留。 It's all untouched. 都没动过。 Any file that's the same in both commits , Git can also leave alone—and these are the ones that let you switch branches. 两次提交中相同的任何文件,Git都可以保留下来-这些文件使您可以切换分支。

Much of Git, including commit-switching, is relatively fast because of this index. 由于该索引,很多Git(包括提交切换)相对较快。 What's actually in the index is not each file itself, but rather each file's hash . 实际上,索引中的内容不是每个文件本身,而是每个文件的hash The copy of the file itself is stored as what Git calls a blob object , in the repository. 文件本身的副本作为Git所谓的blob对象存储在存储库中。 This is similar to how the files are stored in commits as well: commits don't actually contain the files , they just lead Git to the hash ID of each file. 这也与文件在提交中的存储方式类似:提交实际上并不包含文件 ,它们只是将Git引向每个文件的哈希ID。 So Git can compare hash IDs—currently 160-bit-long strings—to decide if commits X and Y have the same file or not. 因此,Git可以比较哈希ID(当前为160位长的字符串),以确定提交XY是否具有相同的文件。 It can then compare those hash IDs to the hash ID in the index, too. 然后,它也可以将这些哈希ID与索引中的哈希ID进行比较。

This is what leads to all the oddball corner cases above. 这就是导致上述所有奇怪情况的原因。 We have commits X and Y that both have file path/to/name.txt , and we have an index entry for path/to/name.txt . 我们提交的XY都有文件path/to/name.txt ,并且我们有一个索引条目path/to/name.txt Maybe all three hashes match. 也许所有三个哈希都匹配。 Maybe two of them match and one doesn't. 也许其中两个匹配,一个不匹配。 Maybe all three are different. 也许这三个都不一样。 And, we might also have another/file.txt that's only in X or only in Y and is or is not in the index now. 而且,我们可能还具有another/file.txt ,它仅在XY中,现在不在索引中。 Each of these various cases requires its own separate consideration: does Git need to copy the file out from commit to index, or remove it from index, to switch from X to Y ? 这些不同情况中的每一种都需要有自己单独的考虑:Git 是否需要将文件从提交复制到索引,或者从索引中删除,以从X切换到Y If so, it also has to copy the file to the work-tree, or remove it from the work-tree. 如果是这样,它还必须将文件复制到工作树,或将其从工作树中删除。 And if that 's the case, the index and work-tree versions had better match at least one of the committed versions; 如果这种情况,则索引和工作树版本最好至少匹配提交的版本之一; otherwise Git will be clobbering some data. 否则,Git将破坏一些数据。

(The complete rules for all of this are described in, not the git checkout documentation as you might expect, but rather the git read-tree documentation, under the section titled "Two Tree Merge" .) (有关这一切的完整规则,而不是您可能期望的git checkout文档,而是标题为“ Two Tree Merge”的部分下的git read-tree文档, )。


#5楼

The correct answer is 正确答案是

git checkout -m origin/master

It merges changes from the origin master branch with your local even uncommitted changes. 它将来自起源主分支的更改与您本地的甚至未提交的更改合并。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/68163
推荐阅读
相关标签
  

闽ICP备14008679号