简体   繁体   English

Github-如何还原错误分支的拉动

[英]Github - how to revert a pull from wrong branch

I accidentally made a pull from a different branch, which shifted the HEAD saying: 我不小心从另一个分支拉了一下,这转移了HEAD说法:

HEAD is now at 7c0208906 Merge remote-tracking branch 'refs/remotes/origin/lorem-ipsum'

Now, it keeps pulling from the origin/lorem-ipsum instead of origin/master 现在,它一直从origin/lorem-ipsum而不是origin/master拉出

The files which got pulled also came with conflicts. 被拉出的文件也带有冲突。 Now its been a few days, new changes have been made on the master branch by others in the parent repository and i'm behind. 现在过了几天,父存储库中的其他人在master分支上进行了新更改,但我落后了。

How can I revert the state of my repository back to how it was prior to my wrong pull and how can I simply shift the HEAD to previous state? 如何将存储库的状态恢复为错误拉出之前的状态,如何将HEAD轻松移至先前的状态?

Please help, I'm stuck! 请帮助,我被卡住了!

The git pull command just runs git fetch (which is always 1 safe to do at any time) followed by git merge 2 (which is less so). git pull命令只运行git fetch (在任何时候总是安全地执行1 ),然后运行git merge 2git merge并非如此)。 What you therefore need to recover from is your git merge . 因此,您需要从中恢复的是git merge But this: 但这

Now, it keeps pulling from the origin/lorem-ipsum instead of origin/master 现在,它一直从origin/lorem-ipsum而不是origin/master拉出

... suggests that you told Git to remember that the "upstream" for master is origin/lorem-ipsum , not origin/master . ……建议您告诉Git 记住 master的“上游”是origin/lorem-ipsum ,而不是origin/master If that's true—you don't show the actual git pull you ran, nor the output of git status or git branch -vv , all of which would be useful clues here—then you also need to fix the upstream setting for your master . 如果是这样-您没有显示运行的实际git pull ,也没有显示git statusgit branch -vv的输出,所有这些在这里都是有用的线索-那么您需要为master修复上游设置。

Before we dive into the next step, there's one more thing to note: 在我们进行下一步之前,还有一点要注意:

The files which got pulled also came with conflicts. 被拉出的文件也带有冲突。

It's important to remember that Git doesn't pull files . 重要的是要记住,Git不会提取文件 Git obtains, and then merges, commits . Git获取,然后合并, 提交 Commits have files, and merging some commits can result in conflicts in files, so this might seem like a minor quibble, but it affects how you recover these things. 提交过文件,合并一些提交可能导致文件有冲突,所以这似乎是一个小问题,但它会影响您如何恢复这些事情。

When you do get a merge conflict, you must resolve the conflicted files yourself, git add the results, and do a final git commit to make the merge commit. 得到一个合并冲突, 必须自己解决冲突的文件, git add的结果,并做最后的git commit ,使这次合并提交。 (When you don't get a merge conflict, Git adds and commits the result for you.) The git status command is extremely useful as it will tell you if you are in the middle of a conflicted merge, plus a lot of other good information. (当您没有合并冲突时,Git会为您添加并提交结果。) git status命令非常有用,因为它将告诉您是否处于冲突合并中,以及许多其他好处信息。 Use it often. 经常使用它。

Getting rid of a bad merge 摆脱糟糕的合并

If you're in the middle of a bad merge and you want the whole thing to go away, that's easy: just run git merge --abort . 如果你在一个糟糕的合并的中间 ,你想整个事情去了,这很简单:只要运行git merge --abort That stops the merge process and puts everything back the way it was before you started. 这将停止合并过程,并将所有内容恢复到开始之前的状态。

If you've finished the merge, but it was bad and you want to get rid of it, that's harder, because you, or Git, finish a merge by making a new commit, and the whole point of commits is that they are permanent and unchanging. 如果您已经完成了合并,但是它很糟糕并且您想要摆脱它,那就很难了,因为您或Git通过进行新的提交来完成合并,而整个提交的要点是它们是永久性的并保持不变 Commits stick around "forever", 3 and new commits build upon previous commits, so it's really hard to get rid of a "middle" one. 提交坚持“永远”, 3,并且新的提交基于先前的提交,因此要摆脱“中间”的提交真的很难。 It's much easier to get rid of a few "end" ones. 摆脱一些“终结”者要容易得多。

Let's draw some commits, including a merge, to see what we mean here. 让我们绘制一些提交,包括合并,以了解我们的意思。 Note that each commit has some incomprehensible hash ID ( 1fc39a7 or deadc0d or some such). 请注意,每个提交都有一些难以理解的哈希ID( 1fc39a7deadc0d或类似的名称)。 To keep things clearer, we'll use single uppercase letters for each commit. 为了使内容更清楚,我们将为每个提交使用单个大写字母。 Note also that each commit has a parent commit, except for merges which have two parent commits. 还要注意,除了具有两个父提交的合并外,每个提交都有一个提交。 We say that each commit "points back to" its parent (or, for a merge, parents plural): 我们说每个提交都“指向”其父级(或合并时,父级复数):

... <-E <-F <-G <-K   <-- master (HEAD)
       \         /
        H <-I <-J   <-- origin/lorem-ipsum

Here K is our merge commit, pointing back to both G and J . 这里K是我们的合并提交,同时指向GJ G is the commit that was the last commit on your master . G被提交,这最后一次提交你的master The name master now means ("points to") this commit K . 现在,名称master表示(“指向”)此提交K The name origin/lorem-ipsum points to commit J . 名称origin/lorem-ipsum指向提交J We also mark master with HEAD , to note that this is the branch we have checked-out now. 我们还用HEAD标记master ,注意这是我们现在已签出的分支。

To remove K entirely, we can use git reset . 要完全删除 K ,我们可以使用git reset The git reset command can be quite destructive, so before you use it, run git status to make sure there's nothing you care about that you will lose. git reset命令可能具有很大的破坏性,因此在使用它之前,请运行git status以确保您不会担心会丢失任何内容。 (This is a recurring theme: run git status . ) Having made sure you are on master , that there's nothing else you can lose right now, and that master points to this merge commit K that you do want to lose on purpose: (这是一个反复出现的主题: 运行git status )确保您已经master ,现在您再也不会丢失其他任何东西,并且master指向您确实要失去的合并提交K

git reset --hard master~1

The ~1 suffix tells Git to find the commit to which master points, and then move back one step . 后缀~1告诉Git找到master点的提交,然后后退一步 That is, follow the arrow coming out of K . 也就是说,跟随从K出来的箭头。 If there are two arrows—there are, because K is a merge—Git should follow the first one, which is always the one pointing to the commit that was on master just before we did the merge. 如果有两个箭头(由于K是合并而存在),则Git应该跟随一个箭头,这始终是指向合并之前master上的提交的箭头。 So Git follows the arrow to commit G . 因此,Git跟随箭头提交G

(Another way to do this is to use git log to find the actual hash ID for commit G , and run git reset --hard <hash-id> . That's harder to type in, although cut-and-paste should work well. But this "step back 1" with ~1 is easier, I think.) (执行此操作的另一种方法是使用git log查找提交G的实际哈希ID,然后运行git reset --hard <hash-id> 。尽管剪切粘贴应能很好地工作,但这很难键入。但是,我认为这种“退后1”( ~1 )更容易。)

What git reset --hard does is three things: git reset --hard做的三件事:

  1. Change the branch to point to the selected commit. 更改分支以指向选定的提交。 That means master now names commit G , not K : 这意味着master现在命名commit G ,而不是K

     ...--E--F--G <-- master (HEAD) \\ H--I--J <-- origin/lorem-ipsum 

    (I left out the internal arrows this time, as they're a pain to draw and don't give us much. What happened to K ? It's still in there, in your repository, but it's in the recycling bin now, where it is hard to find and Git normally won't show it any more.) (这一次我省略了内部箭头,因为它们很容易绘制且不给我们太多K发生了什么?它仍然在您的存储库中,但是现在在回收箱中,在这里很难找到,Git通常不会再显示它。)

  2. The reset --hard step also resets Git's index . reset --hard步骤还会重置Git的index Git's index is best described, I think, as "where you build up your next commit". 我认为,最好将Git的索引描述为“下一次提交的地方”。 In normal cases, you want your index to match your current commit until you're starting to edit and git add changes to make a new commit. 在正常情况下,您希望索引与您的当前提交匹配,直到您开始编辑并git add更改以进行新提交为止。 So this is what you want: for your index to match commit G . 所以这就是您想要的:让您的索引匹配commit G

  3. The reset --hard step also resets your work-tree , ie, the place where you have all your files visible in the normal way so that you can work with or on them. reset --hard步骤还重置您的工作树 ,即以正常方式可见所有文件的位置,以便您可以使用它们或对其进行处理。 This throws away the merged versions from K and replaces them with the versions from G , which is also what you want. 这会丢弃K的合并版本,并用G的版本替换它们,这也是您想要的。

Now it's as though the merge never happened. 现在好像合并从未发生过。

Your upstream setting is still probably origin/lorem-ipsum , though. 不过,您的上游设置仍然可能是origin/lorem-ipsum

Fixing or changing your upstream 修复或更改上游

To change the upstream setting of your current branch (still master ), simply run git branch --set-upstream-to= new-upstream . 要更改当前分支(仍然是master )的上游设置,只需运行git branch --set-upstream-to= new-upstream In this case, you want to set it to origin/master again, so: 在这种情况下,您想要将其再次设置为origin/master ,因此:

git branch --set-upstream-to=origin/master

Now your current branch's ( master 's) upstream is origin/master , so that git pull means "fetch, then merge with origin/master "—presumably what you want. 现在,您当前的上游分支( master )是origin/master ,因此git pull意思是“获取,然后与origin/master合并”-大概就是您想要的。

I recommend avoiding git pull 我建议避免git pull

The git pull command is meant to be convenient. git pull命令是为了方便。 And it is ... but this convenience is a sort of a trap. 确实 ……但是这种便利只是一个陷阱。 If you knew you were doing git fetch followed by git merge , you would have known to look at ways to undo a merge (and there are lots of existing SO answers for this). 如果您知道git fetch执行git fetch之后执行git merge ,那么您应该知道可以撤消合并的方法(为此,有很多现有的SO答案)。

Besides this, it's often better to use git rebase anyway. 除此之外,还是最好使用git rebase So then you should run git fetch followed by git rebase . 因此,您应该先运行git fetch再运行git rebase You can get git pull to do this for you ... but sometimes rebase isn't the way to go; 可以使用git pull来为您执行此操作...但是有时无法恢复基准; sometimes merge is better. 有时合并会更好。 As you learn more Git, you will find that the merge-vs-rebase decision sometimes depends on what you get when you fetch . 随着对Git的更多了解,您会发现merge-vs-rebase决策有时取决于您获取时得到的信息 In which case, how can you decide in advance whether to rebase or merge, before you see what gets fetched? 在这种情况下,如何查看要提取的内容之前如何预先确定是要变基还是合并?

It's better to learn the separate steps first, I think. 我认为最好先学习单独的步骤。 Then, once you know them well, you can decide whether typing in just the one command ( git pull ) is worth the convenience vs the occasional headache when it goes wrong. 然后,一旦您对它们很了解,就可以决定只键入一个命令( git pull )是否值得带来便利,而在出错时偶尔会头疼。 You will also know by then whether you want to merge or rebase by default, and you can set up git pull to do that. 届时,您还将知道默认情况下是要合并还是要变基,并且可以设置git pull来做到这一点。


1 There are ways you can run git fetch manually that are not entirely safe, but they are very difficult to do. 1有几种方法可以手动运行git fetch ,虽然并不完全安全,但很难做到。 You won't get this accidentally. 您不会偶然得到的。

2 You can direct git pull to use git rebase as its second step, instead of running git merge as its second step. 2您可以指示git pullgit rebase用作第二步,而不是将git merge作为第二步。 I assume, and what you show suggests, you did not do this. 我认为,正如您所显示的暗示,您没有这样做。

3 Well, that is, commits are permanent until you deliberately throw them out. 3好吧,也就是说,提交是永久的,直到您有意将其丢弃。 Then they are eventually tossed out for real with the rest of the rubbish, by git gc , the Garbage Collector, after a suitable waiting period during which you can recover them from the trash. 然后, 适当的等待时间(可以从垃圾中回收它们) 之后 ,它们最终会被git gc (垃圾收集器)与其余的垃圾一起扔掉。

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

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