简体   繁体   English

将master分支中未提交的更改移至dev分支并还原master

[英]Move uncommitted changes in master branch to dev branch and revert master

I'm trying to start using git branches, but there is something i don't understand. 我正在尝试开始使用git分支,但是有些我不理解。

What started as a small change on my master branch is growing into a large feature, so i'd like to move this to a new branch called dev so I can still do minor changes on the master branch and merge the dev branch when it's finished. 从我的master分支上的一个小变化开始,到现在已经发展成为一个大功能,所以我想将其移到一个名为dev的新分支上,这样我仍然可以在master分支上进行较小的更改,并在完成时合并dev分支。

I've tried all kind of methods but i'm taking this one as example because i have unexpected behavior. 我尝试了所有方法,但是我以这个方法为例,因为我有意外的行为。

So with git stash all uncommitted changes are moved to the stash and the master is reverted back to it's last commit state. 因此,使用git stash所有未提交的更改都将移到该stash中,而master则恢复为最后的提交状态。 Then a new "dev" branch is created and switched to it. 然后创建一个新的“ dev”分支并切换到该分支。 with stash apply the saved stash is applied back to this branch and all changes are back. 使用stash apply保存的存储重新应用到该分支,并且所有更改都返回。 Now when I checkout master I see all changed files are present again. 现在,当我checkout master我看到所有更改的文件再次出现。 I expected that only the dev branch had these changes and when switched back to master it would have the state as the last commit. 我期望只有dev分支具有这些更改,并且当切换回master时,它将具有最后一次提交的状态。 I'm not sure if this is normal behavior and my expectations are wrong (with more questions) or should the master branch have stayed in de last commit state? 我不确定这是否是正常行为,并且我的期望是错误的(有更多问题),还是master分支应该保持在最后一次提交状态?

What you're seeing is that there's a very big difference between your work-tree —the area where you do your work—and actual commits . 您会看到,您的工作树(从事工作的区域)与实际提交之间有很大的差异。

It may help if you don't think about Git as storing changes , because it doesn't. 如果您不认为Git存储更改 ,可能会有所帮助,因为事实并非如此。 It stores snapshots . 它存储快照 Each commit is a full, complete copy of all of your files—well, all of the ones you've committed, but that's kind of redundant, so just think "all the files". 每次提交都是所有文件的完整副本,当然,还包括所有已提交的文件,但这有点多余,因此,请考虑“所有文件”。 The saved snapshots are frozen forever—well, living as long as the commit itself lives, but that's forever by default, anyway. 保存的快照将永久冻结—好吧,只要提交本身存在就可以生存,但是无论如何默认情况下都是永久的。

These snapshots are saved in a special, compressed, frozen, Git-only format. 这些快照以特殊的,压缩的,冻结的,仅Git的格式保存。 They're no good to anything but Git, so for you to work with them—on git checkout —Git has to expand them out into a normal form, where they're unfrozen and useful, and of course you can change them around. 它们除了对git checkout不利之外,对其他任何事物都没有好处,因此,在git checkout要让您与它们一起使用, git checkout必须将它们扩展为正常的形式,使它们处于未冻结和有用的状态,当然您可以对其进行更改。 That's your work-tree , where you will do your work. 那就是您的工作树 ,您将在其中进行工作。

So there are two "active" copies of every file: the frozen one in the commit, and the one you're working on. 因此,每个文件都有两个“活动”副本:提交中的冻结副本和您正在处理的副本。 When you view things as changes , you're comparing these different copies. 当您将事物视为变更时 ,您正在比较这些不同的副本。

Git adds an extra wrinkle here, because there's actually a third copy of every file. Git在这里增加了额外的皱纹,因为实际上每个文件都有第三份副本。 When Git first extracts a commit, it copies the frozen Git-only files into what Git calls, variously, the index , the staging area , or the cache . 当Git第一次提取提交时,它会将冻结的仅Git的文件复制到Git调用的文件中,这些文件有各种不同的名称, 索引暂存区缓存 Here, files are still Git-only, but now they're not quite frozen any more. 在这里,文件仍然仅适用于Git,但是现在它们不再冻结了。 They are more sort of slushy, ready-to-freeze, and specifically, you can replace the index copy of any file with a new copy. 它们更加肮脏,易于冻结,特别是,您可以新副本替换任何文件的索引副本。 That's what git add does. 这就是git add所做的。

When you go to use git stash , what Git really does is make a commit, or more precisely, two commits. 当您使用git stash ,Git真正要做的是进行一次提交,或更准确地说,进行两次提交。 Then it cleans out the index and work-tree so that they match the current, or HEAD , commit. 然后,它清除索引和工作树,使其与当前提交(即HEAD匹配。 The main special thing about this stash commit is that it is not on any branch. 此隐藏提交的主要特殊之处在于它不在任何分支上。 So you can switch to another branch and use git apply to re-extract it, regardless of which branch you've switched-to. 因此,无论您切换到哪个分支,都可以切换到另一个分支并使用git apply重新提取它。

Switching to another branch means select that branch's tip commit as the current commit, and that branch as the current branch . 切换到另一个分支意味着选择该分支的tip提交作为当前提交,并选择该分支作为当前分支 Git will copy all of those files, from that snapshot, out into the index and work-tree. Git会复制所有这些文件,从快照,出到索引和工作树。 If you were clean before, you'll again be in a clean situation, but now the HEAD commit is some other commit. 如果您以前是干净的,那么您将再次处于干净的状态,但是现在HEAD提交是其他提交。 Now you can modify the work-tree again. 现在,您可以再次修改工作树。

As Philip Couling noted , there's a special case where two branch names identify the same commit. 正如Philip Couling指出的那样 ,在特殊情况下,两个分支名称标识同一提交。 If we draw the commits as a chain, with two branch names pointing to the latest commit: 如果我们将提交绘制为链状,则两个分支名称指向最新的提交:

...--o--o--*   <-- master, develop

then, in one sense, it doesn't matter whether we git checkout master or git checkout develop . 然后,从某种意义上讲,无论我们是git checkout master还是git checkout develop git checkout master都没有关系。 Both identify that same last commit. 两者都标识相同的最后一次提交。 The active snapshot in HEAD , the index, and the commit will all have the same contents . HEAD的活动快照,索引和提交都将具有相同的内容 Git doesn't have to do anything to the index and work-tree if you switch from commit * to commit * since you're not actually switching anything in them! Git不会做任何事情的索引和工作树,如果你从提交切换** ,因为你没有实际切换在他们的东西!

But that's not all that git checkout does! 但这还不是git checkout的全部功能! In particular, checking out master tells Git to attach the name HEAD to the name master , while checking out develop tells Git to attach it to develop instead. 特别是,检出master告诉Git将名称HEAD附加到名称master ,而检出develop告诉Git将其附加到develop This affects which branch name gets updated when you make a new commit. 这会影响在您进行提交时更新哪个分支名称 If you are set up like this: 如果您是这样设置的:

...--o--o--*   <-- master, develop (HEAD)

and then make a new commit, Git will move the name develop to point to the new commit: 然后进行新的提交,Git将把名称develop指向新的提交:

...--o--o--o   <-- master
            \
             *   <-- develop (HEAD)

Note that HEAD is still just attached to develop . 请注意, HEAD仍然只是附着于develop The trick is that develop now identifies this new commit. 诀窍是, develop现在可以识别此新提交。

Git makes the new commit from whatever is in the index , not from what's in the work-tree. Git根据索引中的内容而不是工作树中的内容进行新提交。 You use git add to tell Git: copy the work-tree file over top of the index file. 您使用git add告诉Git: 将工作树文件复制到索引文件的顶部。 This prepares the file for freezing, compressing it down to the special Git-only format, using whatever's in the work-tree version right then. 这将准备冻结文件,将其压缩为特殊的仅Git格式,然后使用工作树版本中的任何内容。 Then git commit just has to flash-freeze the index copies, to make the new commit's frozen copies of all files. 然后git commit只需冻结索引副本,以使新提交冻结所有文件的副本。

So, for this particular case, git stash made a commit, cleaned out your work-tree, then you re-attached HEAD to the same commit but a different branch name, and then re-applied your work-tree changes. 因此,对于这种特殊情况, git stash进行了提交,清理了您的工作树,然后将HEAD重新附加到相同的提交但使用了不同的分支名称,然后重新应用了您的工作树更改。 The stash-and-apply was entirely unnecessary. 隐藏和应用完全没有必要。 If develop and master had pointed to different commits, though, you would often need to do that stash-and-apply. 但是,如果developmaster指出了不同的提交,则通常需要执行该存储并应用。 (Even then, Git will let you get away with switching commits in many cases. See Checkout another branch when there are uncommitted changes on the current branch for all the gory details.) (即使那样,在许多情况下,Git仍会让您摆脱切换提交的麻烦。有关当前所有行事细节,请参见在当前分支上有未提交的更改时签出另一个分支 。)

In most contexts you will get an error if you try to checkout while you have uncommitted changes. 在大多数情况下,如果您在未提交更改的情况下尝试签出,则会收到错误消息。 This will happen if the two branches (the one you're on and the one you're checking out) don't point to the same commit. 如果两个分支(您所在的分支和您要签出的分支)没有指向同一提交,则会发生这种情况。 To get between the two branches retaining the uncommitted changes you need to use git stash as you have done. 为了在两个分支之间保持未提交的更改,您需要像完成操作一样使用git stash

When you create a branch, it points to the commit you're currently on. 创建分支时,它指向您当前正在进行的提交。 So both dev and master point to the same commit. 因此, devmaster指向同一提交。 In this special case, git will let you switch branches freely because the checkout will change no files. 在这种特殊情况下,git可以让您自由切换分支,因为checkout不会更改任何文件。

This is most useful for your use case... You start making changes but forget to start a new branch. 这对您的用例最有用。您开始进行更改,但是忘记启动新分支。 In this case you can just create the new branch and switch to it without using git stash . 在这种情况下,您可以创建新分支并切换到该分支,而无需使用git stash


Edit: 编辑:

If the changes are not there after a git-stash then they will not be there after a commit to another branch. 如果更改在git-stash之后不存在,则在提交到另一个分支后它们将不再存在。 If you really wanted to test this, there's nothing to stop you doing this: 如果您真的想测试一下,没有什么可以阻止您这样做:

git checkout dev
git add *
git commit -m 'Test commit'
git checkout master

# look around

git checkout dev
git reset HEAD~

The last line pops off your last commit and leaves the files there as uncommitted changes. 最后一行弹出您的最后一次提交,并将文件保留为未提交的更改。 Just don't push the test commit . 只是不要推动test commit

I generally second Philip's analysis. 我通常会赞同菲利普的分析。

Just wanted to add this : If you're on your newly created branch and need to checkout back to master, expecting NOT to find your changes here, you'll need to commit these changes on the new branch before making the switch : 只是想添加一下:如果您在新创建的分支上,并且需要签出回master,期望在这里找不到您的更改,则需要在新分支上提交这些更改,然后再进行切换:

# just after your stash apply on the new branch
git commit -a -m"temporary message"
git checkout master

Then if you want to go on working on the new branch, undo this temporary commit, work until you're done, and commit "for real" : 然后,如果您要继续在新分支上工作,请撤消此临时提交,直到完成后再进行工作,然后“真正”提交:

git checkout dev
# the following line will undo the commit but keep your changes in working tree
git reset --soft HEAD^
# work some more
git commit -a -m "real final message"

Git only stores what you've committed. Git仅存储您的承诺。 Since you haven't committed those changes, they aren't stored in either branch. 由于您尚未提交这些更改,因此它们不会存储在任何一个分支中。 Stash is a special way of creating a commit that doesn't belong to any particular branch and includes both staged and unstaged changes (there are various options to modify this behavior); Stash是一种创建不属于任何特定分支的提交的特殊方式,它包括已分阶段的更改和未分阶段的更改(有多种选项可以修改此行为); but when you apply or pop a stash, it doesn't automatically create a new commit on your branch. 但是当您应用或弹出存储时,它不会自动在分支上创建新的提交。

Git is nicely sensitive about losing work. Git对失去工作非常敏感。 When you asked git to checkout master, it noticed that you had uncommitted changes. 当您要求git签出master时,它注意到您尚未提交更改。 It did a quick check in the background to see if you'd lose any work when moving from dev to master. 它在后台进行了快速检查,以查看从开发人员转移到熟练人员时是否会丢失任何工作。 Since both branches had the same history (and thus there were no differences in the files between the two branches), git knew that there would be no conflicts and thus your uncommitted changes were not overwritten or "lost". 由于两个分支具有相同的历史记录(因此两个分支之间的文件没有差异),因此git知道不会有冲突,因此您未提交的更改不会被覆盖或“丢失”。 It simply preserved them. 它只是保留了它们。 This is a handy feature when you realize you're working on the wrong branch... you can attempt to switch branches and take all of your uncommitted work with you so long as there are no conflicts. 当您意识到自己在错误的分支上工作时,这是一个方便的功能...只要没有冲突,您就可以尝试切换分支并与您进行所有未提交的工作。 Note, again, that the uncommitted changes are not stored with either branch until you commit them. 再次注意,未提交的更改在提交之前不会存储在任何一个分支中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM