简体   繁体   English

恢复到 Gitlab 上的特定提交

[英]Revert to a specific commit on Gitlab

I'm fairely new to git .我对git很陌生。
While working on a project on Gitlab , I made some changes in the development branch(green colored branch) instead of checking out a new branch from the development , making changes in the new branch and then merging it back to the development .Gitlab上进行项目时,我在development分支(绿色分支)中进行了一些更改,而不是从development中签出新分支,在新分支中进行更改,然后将其合并回development
Meanwhile, one of the other developers took a pull from the development branch and started working on it( ARE-1195 ).同时,其他开发人员之一从开发分支中提取并开始工作( ARE-1195 )。 Now, I know this is going to create a lot of merge conflicts when the developer will try to merge his branch into the development branch.现在,我知道当开发人员尝试将他的分支合并到development分支时,这会产生很多合并冲突。 How can I avoid this?我怎样才能避免这种情况? I tried looking for a possible solution and came across two terms, revert and reset but i'm confused between these.我尝试寻找可能的解决方案并遇到了两个术语, revertreset ,但我对它们感到困惑。
I want to revert my repository back to the commit where the other developer created ARE-1195 but still keep the changes I made after that.我想将我的存储库恢复到其他开发人员创建ARE-1195的提交,但仍然保留我之后所做的更改。
Posting the latest git graph snippet for reference.发布最新的 git graph 片段以供参考。

在此处输入图像描述

I tried looking for a possible solution and came across two terms, revert and reset but I'm confused between these.我尝试寻找可能的解决方案并遇到了两个术语, revertreset ,但我对它们感到困惑。

Your confusion is appropriate.你的困惑是恰当的。 Git's author (Linus Torvalds) unfortunately chose the wrong verb for at least one of these two actions: the one called revert should probably have been called backout (as it is in Mercurial).不幸的是,Git 的作者 (Linus Torvalds) 为这两个动作中的至少一个选择了错误的动词:调用revert的动作可能应该被称为backout (就像在 Mercurial 中一样)。

To make sense of both, though, we should start with what a commit is and does for you.但是,为了理解这两者,我们应该从提交是什么以及为您做什么开始。 The image you included—which I will transform here into a new, different image—shows some of this:您包含的图像(我将在此处将其转换为新的不同图像)显示了其中的一些:

       B--C--D--E   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui-...

Each uppercase letter here, A through G , stands in for a commit, just as each colored dot in the original image stands in for a commit.这里的每个大写字母AG都代表一个提交,就像原始图像中的每个彩色点都代表一个提交一样。 In my drawing, the newer commits are towards the right , while in the original image, the newer commits are towards the top .在我的绘图中,较新的提交朝向右侧,而在原始图像中,较新的提交朝向顶部 So the drawings are different but they show the same thing:所以图纸是不同的,但它们显示的是相同的东西:

  • The most recent or newest ARE-1195 commit is commit E .最近最新ARE-1195提交是提交E
  • The most recent / newest development-ui commit is commit G .最近/最新development-ui提交是 commit G

Every commit, in Git, has a unique number, but this number is huge—something between 1 and 1461501637330902918203684832716283019655932542975, right now, with future versions of Git going even higher—and seems entirely random (though it's not).在 Git 中,每个提交都有一个唯一的数字,但这个数字很大——现在介于 1 和 1461501637330902918203684832716283019655932542975 之间,未来版本的 Git 会更高——而且看起来完全随机(尽管不是)。 It's normally expressed in hexadecimal and often abbreviated, eg, as a123456 for instance.它通常以十六进制表示并且通常缩写为例如a123456

This number—the unique number for this one particular commit— is the commit, in an important sense: no other commit, anywhere, in any Git repository anywhere in the universe, can ever use that same number, unless it's literally the same commit.这个数字——这个特定提交的唯一编号——在一个重要的意义上就是提交:在宇宙中任何地方的任何 Git 存储库中,没有其他提交可以使用相同的数字,除非它实际上是相同的提交。 So two Git repositories can, at any time, meet up and just compare their numbers (which, despite being huge, are much smaller than the commits themselves).因此,两个 Git 存储库可以随时会面并比较它们的数量(尽管数量很大,但比提交本身要小得多)。 If repository R has some number that repository S lacks, R can transfer the commit to S and now they both have it.如果存储库R具有存储库S缺少的某个编号,则R可以将提交转移S ,现在它们都拥有它。

What's in each commit is:每个提交的内容是:

  • A full snapshot of every file.每个文件的完整快照。 These files are compressed and, importantly, de-duplicated , so that if you make a new commit that mostly re-uses most of the files from an old commit, the new copies literally take no space.这些文件被压缩,重要的是,去重,因此,如果您进行新的提交,主要重用旧提交中的大部分文件,新副本实际上不会占用空间。 But they're still logically copies of the files, so that each commit has a full copy of every file.但它们在逻辑上仍然是文件的副本,因此每次提交都有每个文件的完整副本。

  • Some metadata , or information about this particular commit.一些元数据,或有关此特定提交的信息。 The metadata include the name of the person who made the commit, and the date-and-time at which they made it, for instance.例如,元数据包括提交人的姓名,以及提交的日期和时间。 Git stuffs into this metadata a list of commit hash IDs , which is usually exactly one entry long. Git将提交哈希 ID 列表填充到此元数据中,该列表通常恰好是一个条目长。 This one-entry list gives the commit number of the parent commit for this particular commit.这个单项列表给出了这个特定提交的提交的提交号

So, in our drawing above, commit G lists the number of commit F in its metadata.因此,在上图中,提交G在其元数据中列出了提交F的数量。 This means that as long as Git can find G , it can find F .这意味着只要 Git 能找到G ,它就能找到F Commit F , being a commit, has snapshot and metadata, and its metadata list the hash ID of commit A as its parent, so Git can move backwards from F to A .提交F ,作为一个提交,有快照和元数据,它的元数据列出了提交A的哈希 ID 作为它的父级,所以 Git 可以从F向后移动到A A has metadata too, which lists some previous commit (not shown here); A也有元数据,其中列出了一些以前的提交(此处未显示); this repeats on and on, and the commits we find as we traverse this list backwards, one commit at a time, is the history in the repository.这不断重复,当我们向后遍历这个列表时,我们发现的提交,一次一个提交,存储库中的历史。 Eventually we get to the very first commit ever, which can't list the hash ID of any previous commit, so its list is empty , and that's where we stop going backwards.最终我们到达了第一个提交,它无法列出任何先前提交的哈希 ID,所以它的列表是的,这就是我们停止向后退的地方。

Meanwhile, as long as Git can find commit E , it can use this to find D , which finds C , which finds B , which finds A , which finds whatever comes before A , and so on.同时,只要 Git 可以找到提交E ,它就可以使用它找到D ,找到C ,找到B ,找到A ,找到 A 之前A任何内容,依此类推。 So that's the history as seen from ARE-1195 .这就是从ARE-1195看到的历史。

Note that there's no way to go forwards .请注意,没有办法前进 If we're at commit A , we can work backwards to the start of history, but we can't go forwards to B or F , because commit A does not know their commit hash IDs.如果我们在提交A ,我们可以向后工作到历史的开始,但我们不能前进BF ,因为提交A不知道他们的提交哈希 ID。 To make the magical numbering system work, Git must not ever change any part of any commit, so it's not possible to add forward links to future commits—and the hash ID of any future commit depends exquisitely on every bit of data that goes into that future commit, including the exact time at which someone makes it, so we have no idea what a future commit's hash ID will be.为了让神奇的编号系统正常工作,Git绝不能更改任何提交的任何部分,因此不可能向未来的提交添加前向链接——任何未来提交的哈希 ID 完全取决于进入该提交的每一位数据未来提交,包括某人提交的确切时间,所以我们不知道未来提交的哈希 ID 是什么。

This is why history always works backwards, and it's also the key to git reset .这就是为什么历史总是倒退的原因,也是git reset的关键。

Using git reset to discard commits使用git reset丢弃提交

The git reset command is big and complicated. git reset命令又大又复杂。 It has a number of different jobs it can do.它可以做许多不同的工作。 We'll ignore most of them and concentrate just on the one job that's interesting to you right here and now.我们将忽略它们中的大多数,只专注于此时此地您感兴趣的一项工作。

We noted above that Git needs to find commit G in order to find commit F .我们在上面提到,Git需要找到提交G才能找到提交F Git needs to find commit E in order to find commit D , which it needs in order to find C , and so on. Git需要找到提交E才能找到提交D ,它需要它才能找到C ,依此类推。 (Either F or B suffices to find A , though.) So how does Git find the random-looking number that designates the most recent ARE-1195 commit? (不过,无论是F还是B都足以找到A 。)那么 Git 如何找到指定最近ARE-1195提交的看似随机的数字呢?

The answer is that the name ARE-1195 itself holds the commit hash ID .答案是名称ARE-1195本身包含提交哈希 ID This is what a branch name does for you and Git: it holds a commit hash ID.这就是分支名称对你和 Git 的作用:它拥有一个提交哈希 ID。 (In fact, all of Git's references work like this. References or refs include branch names, tag names, remote-tracking names, and many other names. Branch names are a bit special in that .git/config often holds more information on them, but we'll ignore that for this answer.) (事实上​​,所有 Git 的引用都是这样工作的。引用或引用包括分支名称、标签名称、远程跟踪名称和许多其他名称。分支名称有点特殊,因为.git/config通常包含更多关于它们的信息,但我们会忽略这个答案。)

The git reset command can move a branch name . git reset命令可以移动一个分支名称 Technically, it will move the current branch name;从技术上讲,它将移动当前分支名称; if we want to move some branch name that's not-current, we must use git branch -f , or switch to the branch so that it is the current branch.如果我们想移动一些不是当前的分支名称,我们必须使用git branch -f ,或者切换到该分支,使其成为当前分支。 It also does more than just move the branch name, but again we're ignoring all the extra stuff it does for now.它还不仅仅是移动分支名称,但我们现在再次忽略它所做的所有额外内容。

Suppose we were to move the name ARE-1195 so that it found commit C instead of commit E .假设我们要移动名称ARE-1195以便它找到提交C而不是提交E That is, what if we made the graph look like this:也就是说,如果我们让图表看起来像这样:

            D--E   ???
           /
       B--C   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui

Git would now find commit C first, then work backwards to B , and then to A . Git 现在会先找到提交C ,然后返回到B ,然后到A It's as though commits D and E just stopped existing.就好像提交DE刚刚停止存在。 They didn't—if you memorized their raw hash IDs, or wrote them down on paper, or something, you'd still be able to find these two commits (and in fact you need only save E 's hash ID since E lets you find D )—but they seem to be gone.他们没有——如果你记住了他们的原始哈希 ID,或者把它们写在纸上,或者其他什么东西,你仍然可以找到这两个提交(实际上你只需要保存E的哈希 ID,因为E让你找到了D )——但它们似乎已经消失了。

Now, git reset , when used in the mode where it moves a branch name, also does two more things—or rather, optionally does two more things:现在, git reset ,当在它移动分支名称的模式下使用时,还会做两件事——或者更确切地说,可选地做两件事:

  1. First, git reset moves the branch name.首先, git reset移动分支名称。
  2. Then, unless you used --soft to make it stop after step 1, git reset resets Git's index .然后,除非您在第 1 步之后使用--soft使其停止,否则git reset会重置 Git 的index
  3. Then, if you used --hard —but not if you used --soft or --mixed , which stop at steps 1 or 2 respectively—it resets your working tree .然后,如果你使用了--hard ——但如果你使用--soft--mixed ,它们分别在步骤 1 或 2 处停止——它会重置你的工作树

We haven't explained Git's index and your working tree here (and won't for space reasons) but when you're removing commits like this, you generally want git reset --hard : you want commits D and E to be totally gone forever, or at least seem that way, so you want to get rid of their effect on Git's index and your working tree, which means using git reset --hard .我们没有在这里解释 Git 的索引和你的工作树(并且不会因为空间原因)但是当你删除这样的提交时,你通常希望git reset --hard :你希望提交DE完全消失永远,或者至少看起来是这样,所以你想摆脱它们对 Git 的索引和你的工作树的影响,这意味着使用git reset --hard

That's what git reset --hard will do for you: make it look like those commits are gone .这就是git reset --hard将为您做的:让那些提交看起来像消失了 There's one big flaw here though.不过这里有一个很大的缺陷。 If you had your Git connect to some other Git software earlier, and had your Git send commits D and E to that other Git, that other Git repository now has those two commits.如果您之前让您的 Git 连接到其他 Git 软件,并且让您的 Git提交DE发送到其他 Git,那么其他 Git 存储库现在拥有这两个提交。 Git is built to add commits, not to remove them: this git reset --hard trick requires a fair bit of by-hand work, but normal everyday usage of Git will automatically add new commits without a lot of by-hand work. Git 是为添加提交而不是删除提交而构建的:这个git reset --hard技巧需要大量的手工工作,但是 Git 的日常使用会自动添加新的提交,而无需大量的手工工作。 So if you've used git push to send your new commits to someone else, they can easily come back to you, as though the someone-else had made them.因此,如果您使用git push您的新提交发送其他人,他们可以轻松地返回给您,就好像其他人已经做出了它们一样。 1 1

If you have never sent these commits anywhere else , using git reset can make them seem to vanish from your repository.如果您从未在其他任何地方发送过这些提交,使用git reset可以使它们似乎您的存储库中消失。 Nobody else has them yet and your Git won't hand them over to other Git repositories, because your Git doesn't see them any more.没有其他人拥有它们,您的 Git 不会将它们交给其他 Git 存储库,因为您的 Git 不再看到它们。 So this is a safe way to get rid of commits, if you have not used git push to send them off.因此,如果您还没有使用git push将它们发送出去,这是一种摆脱提交的安全方法。


1 Although Git records author and committer names in the commit metadata, Git doesn't look at those while greedily adding new commits to your repository. 1尽管 Git 在提交元数据中记录了作者和提交者的姓名,但 Git 在贪婪地将新提交添加到您的存储库时不会查看这些名称。 It just says oooh shiny new commit, must add it and adds it.它只是说oooh 闪亮的新提交,必须添加它并添加它。


git revert backs out a commit git revert退出一个提交

Let's go back to this drawing yet again:让我们再次回到这张图:

       B--C--D--E   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui

Let's suppose further that something in commit D is bad, but that commits D and E have escaped and are out in other Git repositories now.让我们进一步假设提交D的某些内容是坏的,但是提交DE已经逃脱并且现在不在其他 Git 存储库中。 You could use git reset to discard D and E entirely, but they'll come back and re-infect your repository, like some kind of nasty virus.您可以使用git reset完全丢弃DE ,但它们会回来并重新感染您的存储库,就像某种讨厌的病毒一样。 Besides, you like commit E : it's all good!此外,您喜欢提交E :一切都很好! What you'd like to do is back out the effect of commit D .您想要做的是退出提交D的效果。

Running git switch ARE-1195 first (if needed), then:首先运行git switch ARE-1195 (如果需要),然后:

git revert d789012

(if that's D 's hash ID) (如果那是D的哈希 ID)

or:或者:

git revert HEAD~1

( ~1 because we want Git to count back 1 first-and-only-parent link) or similar tells Git: Figure out what changed in D , and undo that change. ~1因为我们希望 Git 倒数1第一个也是唯一的父链接)或类似内容告诉 Git:找出D中发生了什么变化,然后撤消该更改。 Make a new commit whose message is revert <hash-of-D> .进行一个新的提交,其消息是revert <hash-of-D> The end result is:最终结果是:

       B--C--D--E--∇   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui

where new commit backs out the changes made in D .其中 new commit 取消了在D中所做的更改。

Because Git repositories everywhere are greedy for new commits, they will be happy to add this one on, undoing the effect of the one commit .因为各地的 Git 存储库都渴望的提交,所以他们会很乐意添加这个,从而撤销一个提交的效果 This won't undo the effect of commit E ;这不会撤消提交E的效果; if you want that you must revert both commits:如果你愿意,你必须恢复两个提交:

       B--C--D--E--∃--∇   <-- ARE-1195
      /
...--A-----F----G   <-- development-ui

It's generally wise to revert commits in reverse order: undoing E with gets you files that look exactly like they did after D happened, so that undoing D with has no merge conflicts and gets you files that look exactly like they did after C happened.以相反的顺序恢复提​​交通常是明智的:使用撤消E会使您的文件看起来与D发生后的文件完全相同,因此使用撤消D不会发生合并冲突,并使您的文件看起来与C发生后的文件完全相同.

Reverting all the commits in a series backs out all the changes in that series.还原系列中的所有提交会取消该系列中的所有更改。 The git revert command is smart enough to do this in reverse order by itself, so that you simply list all the commits to revert. git revert命令足够聪明,可以自己以相反的顺序执行此操作,因此您只需列出所有要还原的提交。 For instance, to revert both D and E you might run:例如,要同时恢复DE ,您可以运行:

git revert c00795f..HEAD    # if that's the hash ID of commit `C`

or:或者:

git revert HEAD~2..HEAD

will back out both E and D .将退出ED Note that when we use the two-dot <hash1>..<hash2> form like this, we give as hash the commit hash ID before the first commit we care about.请注意,当我们像这样使用双点<hash1>..<hash2>形式时,我们在我们关心的第一次提交之前将提交哈希 ID 作为hash提供。 This—along with the idea that we can use either a hash ID or a name like HEAD or ARE-1195 , or even the suffix trick like HEAD~2 or ARE-1195~2 , wherever Git needs a hash ID—is basic how-to-work-with-Git knowledge that you should carry around in your head.这与我们可以使用哈希 IDHEADARE-1195之类的名称,甚至是HEAD~2ARE-1195~2之类的后缀技巧(在 Git 需要哈希 ID 的地方)的想法一起是基本的-to-work-with-Git 知识,你应该随身携带。 If you need to brush up on it, though, see the gitrevisions documentation , which is worth multiple readings as it is just packed with good stuff.但是,如果您需要复习它,请参阅gitrevisions 文档,该文档值得多次阅读,因为它包含了很多好东西。

OP: I tried looking for a possible solution and came across two terms, revert and reset but i'm confused between these. OP:我尝试寻找可能的解决方案,遇到了两个术语, revertreset ,但我对它们感到困惑。

The difference between reset and revert重置还原的区别

Below is a general case illustration of the difference between reset and revert , and when to use what.下面是resetrevert之间区别的一般案例说明,以及何时使用什么。

In short, reset is used to move the tip of a branch to a pre-exiting commit (generally back in time, but not always).简而言之, reset用于将分支的尖端移动到预先退出的提交(通常回到时间,但并非总是如此)。 revert on the other hand creates a new commit that reverts the changes made by another commit (in essence it's a reversed cherry-pick ).另一方面, revert创建了一个新的提交,该提交恢复了另一个提交所做的更改(本质上它是一个反向的cherry-pick )。

重置和还原的区别

Consider the illustration above, with a common start case on the left hand side and the two options side-by-side on the right.考虑上面的插图,左侧是一个常见的起始案例,右侧是两个选项并排。 Pay attention to the master branch in this case, as it is what's being modified with the two commands.在这种情况下,请注意master分支,因为它是使用两个命令修改的内容。

Source: Above excerpt is taken from this full length post on the subject: How to Undo Changes in Git (reset vs revert vs restore)资料来源:以上摘录摘自这篇关于该主题的完整帖子: 如何撤消 Git 中的更改(重置 vs 恢复 vs 恢复)

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

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