简体   繁体   English

如何还原更改所有文件的特定提交,从而在还原过程中引起很多冲突?

[英]How do I revert a specific commit that changed all files, causing lots of conflicts during revert?

In a collaborative repository, someone made a commit that somehow made it appear like it re-added all files in the repository again. 在协作存储库中,有人进行了提交,使其以某种方式出现,就像它再次重新添加了存储库中的所有文件一样。

Here is part of the history when printed with --date-order : 这是使用--date-order打印时的部分历史记录:

* 28cbf861 - (65 minutes ago) update logs and metadata (HEAD -> master, origin/master, origin/HEAD)
* e589776e - (18 hours ago) P2LTR15 update
...
* d4c61147 - (5 days ago) Delete P2STR03_SRC00150_Q0448_0000_0-7.log
* fa837509 - (5 days ago) Delete P2STR03_SRC00150_Q0427_0000_0-7.log
*   9a5e2300 - (5 days ago) git pull at 20180421 10am.
|\
* | 6567df4b - (5 days ago) add md5 3min
* | 6f7c80f7 - (5 days ago) Added md5sum files for all 3min SRCs
 /
* b97e834f - (6 days ago) Delete P2STR13_SRC00605_Q0325_0000_0-9.log
* 5cd9989b - (6 days ago) Delete P2STR13_SRC00605_Q0129_0000_0-9.log
* 769ae25d - (6 days ago) Delete P2STR13_SRC00605_Q0209_0000_0-9.log
...

The faulty commit is 6f7c80f7 , and I would like to remove it, but keep 6567df4b . 错误的提交是6f7c80f7 ,我想将其删除,但保留6567df4b The git pull commit ( 9a5e2300 ) is a simple merge commit. git pull commit( 9a5e2300 )是一个简单的合并提交。

When I print the history without --date-order , suddenly the commit 6f7c80f7 is shown at the very bottom, ie as if it was the first commit ever. 当我打印不带--date-order的历史记录时,突然在6f7c80f7显示了6f7c80f7提交,即好像是第一次提交。

* 28cbf861 - (70 minutes ago) update logs and metadata
* e589776e - (18 hours ago) P2LTR15 update
...
* fa837509 - (5 days ago) Delete P2STR03_SRC00150_Q0427_0000_0-7.log
*   9a5e2300 - (5 days ago) git pull at 20180421 10am.
|\
| * b97e834f - (6 days ago) Delete P2STR13_SRC00605_Q0325_0000_0-9.log
...
| * 697a103c - (7 months ago) initial commit
* 6567df4b - (5 days ago) add md5 3min
* 6f7c80f7 - (5 days ago) Added md5sum files for all 3min SRCs

I tried doing a git revert 6f7c80f7 , but it shows a lot of conflicts that I don't know how to resolve, that is, when I do a git status : 我尝试执行git revert 6f7c80f7 ,但是它显示了很多我不知道如何解决的冲突,也就是说,当我执行git status

On branch master
Your branch is up to date with 'origin/master'.

You are currently reverting commit 6f7c80f7.
  (fix conflicts and run "git revert --continue")
  (use "git revert --abort" to cancel the revert operation)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    P2LTR15/.gitkeep
    deleted:    P2LTR15/MOS_P2LTR15_MO.csv
    ... (this is basically a list of almost all the files in the repository)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add/rm <file>..." as appropriate to mark resolution)

    deleted by them: P2LTR15/P2LTR15.yaml
    deleted by them: P2LTR15/audioFrameInformation/P2LTR15_SRC00712_HRC021.afi
    ... (this is another list of files, I don't know why it is there, possibly those were actually changed)

Is there any other way I could rewrite the Git history in such a way that this bad commit never happened? 我还有其他方法可以重写Git历史记录,以至于这种错误的提交从未发生吗?

I've looked at this guide but it also only tells me to do a revert. 我看过本指南,但它仅告诉我进行还原。 Should I attempt to go back to a commit before that time, then cherry-pick commits and then force-rewrite the repository? 我是否应该尝试在此之前返回到提交,然后选择Cherry-pick提交 ,然后强制重写存储库? I don't think that will be a nice option either. 我也不认为这是一个不错的选择。

You say that your git log output quote is only part of the history, but you also say that commit 6f7c80f7 adds all the files, and in the quoted text, 6f7c80f7 never shows any parent commit to which it connects. 您说您的git log输出引用只是历史记录的一部分 ,但您也说了commit 6f7c80f7会添加所有文件,并且在引用的文本中6f7c80f7永远不会显示其连接的任何父提交。

This suggests that 6f7c80f7 is a root commit: an initial commit that, in effect, adds every file that it shows, because the "previous" commit does not exist, so every file must by definition be new in that commit. 这表明6f7c80f7根提交:实际上,它会添加它显示的每个文件的初始提交,因为“先前”提交不存在,因此根据定义,每个文件在该提交中必须是新的。

A graph can have more than one root. 一幅图可以有多个根。 It's a little bit unusual in a Git commit graph since we normally make new commits by first checking out some existing commit, then making changes, then running git commit to save a new complete snapshot. 在Git提交图中这有点不寻常,因为我们通常通过首先检出一些现有提交,然后进行更改,然后运行git commit来保存新的完整快照来进行新git commit But there are other ways: 但是还有其他方法:

  • We can use git fetch to obtain an unrelated repository, then git merge with --allow-unrelated-histories . 我们可以使用git fetch获取不相关的存储库,然后git merge--allow-unrelated-histories git merge If the Git version is old enough, git merge always allows unrelated histories, and does not have an --allow-unrelated-histories flag: it just merges these without warning. 如果Git版本足够老,则git merge 总是允许不相关的历史记录,并且没有--allow-unrelated-histories标志:它只是合并这些文件而不会发出警告。 (You might see a lot of add/add conflict errors here and have to do a lot of hand resolving though!) (您可能会在这里看到很多add/add conflict错误,并且必须做很多手工操作!)

    This adds their —the other repository's—root commit to our own commit graph, which already had our root commit, so now we have two root commits. 这会将它们(另一个存储库的)根提交添加到我们自己的提交图中,该提交图已经具有我们的根提交,因此现在我们有了两个根提交。

  • We can use git checkout --orphan to set our own repository state up so that the next commit we make will be a root commit. 我们可以使用git checkout --orphan来设置自己的存储库状态,以便我们进行的下一个提交将是根提交。

    (If and when we do make that root commit, we will have the same merge issues as with the "fetch unrelated repository" case.) (如果当我们作出这样的承诺根,我们将有相同的合并问题作为与“取无关库”的情况。)

  • We can, accidentally or deliberately, fiddle with Git internals, using plumbing commands like git commit-tree or just poking at files inside .git , to create a new root commit. 我们可以不经意地或故意地使用git commit-tree等管道命令来修饰Git内部,或者只是在.git内部的文件中创建一个新的root提交。

In any case, once we have this state where there are multiple roots and a merge commit that merges the extra root and then have built more commits atop the merge, we are sort of stuck with the situation. 无论如何,一旦处于这种状态,即存在多个根,并且合并提交合并了多余的根,然后在合并之上构建了更多提交,我们就陷入了这种局面。 The merge commit—in your case, 9a5e2300 —has a hash ID that depends on the existence of both chains of commits: the one leading back to the original root commit, and the one leading back to the new root commit. 合并提交(在您的情况下为9a5e2300具有一个哈希ID,该ID 取决于 两个提交链的存在:一个返回到原始根提交,一个返回到新的根提交。 The hash ID of any commit that has 9a5e2300 as a parent—in your case, this is just the one commit fa837509 —depends on the existence of 9a5e2300 , as it literally incorporates that hash ID into its own ID. 任何散列ID犯有9a5e2300作为父母,你的情况,这仅仅是一个承诺fa837509上存在-depends 9a5e2300 ,因为它从字面上结合了哈希ID为自己的ID。 The hash ID of the child of fa837509 (again there is just one commit in your graph here) then depends on the existence of fa837509 ; fa837509的孩子的哈希ID(同样,在您的图形中只有一个提交)则取决于fa837509的存在; if the rest of the graph is linear, each child in turn depends on its parent ID, all the way to the end. 如果图的其余部分是线性的,依次在每个子取决于它的父ID,一路到底。

Hence, if you were to somehow get rid of the extra root commit, your merge commit would need to have a different hash ID, which would mean every subsequent commit would also need to have a different hash ID. 因此,如果你以某种方式摆脱多余的根的承诺,您的合并提交需要有不同的哈希ID,这意味着以后每犯将需要有不同的哈希ID。 Moreover, if you did rid yourself of 6f7c80f7 , the copy you would have to make of its current child 6567df4b would be another root commit! 此外,如果您确实摆脱了6f7c80f7 ,则您必须为其当前子级6567df4b制作的6567df4b另一个root提交! You would have to get rid of that one too, which would mean that there would be nothing for merge commit 9a5e2300 to merge, so you would probably want to ditch that as well—except that you'd want to somehow retain its source snapshot . 您也必须摆脱那一个,这意味着将没有任何合并提交9a5e2300进行合并,因此您可能也希望放弃它-除了要以某种方式保留其源快照 Then you would need to take all of the remaining (presumably linear) chain, from fa837509 up to the tip, and copy each of those commits to new commits that do not depend on the existence of 9a5e2300 , but do use whatever commit you made that has the updated source snapshot from the merge commit. 那么你就需要把所有剩余的(大概是线性)链,从fa837509到尖端,并复制每个这些提交新提交的是不依赖于存在的9a5e2300 ,但不要提交你所做的任何使用具有来自合并提交的更新的源快照。

Hence, this leads to the process you would have to use to avoid having two root commits: 因此,这将导致您必须避免使用两次根提交的过程:

  • Copy the snapshot in merge 9a5e2300 to a new non-merge commit. 将合并9a5e2300的快照复制到新的非合并提交中。 This gets a new hash ID (because it's a new unique commit). 这将获得一个新的哈希ID(因为这是一个新的唯一提交)。 Save that ID somewhere. 将该ID保存在某处。

  • Copy the snapshot in fa837509 to a new commit, which likewise gets a new hash ID. fa837509的快照复制到新提交,该提交同样会获得新的哈希ID。 The new commit's parent would be the ID you just saved. 新提交的父级将是您刚刚保存的ID。 Save this ID somewhere. 将此ID保存在某处。

  • Copy the snapshot in the child of fa837509 to a new commit, using the previous copy as the new parent. 使用以前的副本作为新的父代,将fa837509的子代中的快照复制到新提交。

  • Repeat for every commit until you reach the tip. 对每次提交重复一次,直到达到提示。

Once you have this linear chain of commits that do not depend on the commits you want to get rid of, you can then simply stop using the original chain of commits that does depend on the commits you want to get rid of. 一旦有了不依赖于要删除的提交的线性提交链,那么您就可以简单地停止使用 依赖于要删除的提交的原始提交链。 If you and everyone else do this, and you remove the names by which your Git finds the old chain, then it appears as if that old chain never existed. 如果您和其他所有人都这样做了,并且您删除了Git 查找旧链的名称,那么好像该旧链就不存在了。 With the names for them gone, eventually the old commits really do get removed. 随着名称的消失,最终旧的提交确实被删除了。 (This happens when all the safety-checking timer stuff expires. These safety checks exist so that you can get old commits back if you make a mistake but catch it soon enough—typically 30 days—and so that Git can make objects that are not yet connected up into the commit graph, but will be within 14 days. Normal Git commands connect them up within a few milliseconds at worst, so 14 days is practically millennia.) (这种情况发生在所有安全检查计时器中的内容都到期时。这些安全检查存在,因此,如果您犯了一个错误,则可以找回旧的提交,但要尽快(通常为30天)捕获它,以便Git可以制作不是尚未连接到提交图,但在14天之内。正常的Git命令在最差的几毫秒内将它们连接起来,因此14天实际上是几千年。)

The above shows that it is possible to do what you are suggesting (though perhaps you would want to construct a slightly different replacement history than the one I described). 上面的内容表明可以按照您的建议进行操作(尽管您可能希望构建与我描述的替换历史略有不同的替换历史)。 Whether it's a good idea is another question entirely. 这是一个好主意,完全是另一个问题。 The main drawback to rewriting history, however you do it—with git rebase , with git filter-branch , with The BFG Repo Cleaner , or with something you come up with on your own—is that the rewritten repository is, in effect, a new repository. 但是,重写历史记录的主要缺点(使用git rebasegit filter-branch ,使用BFG Repo Cleaner或您自己想出的东西)实际上是重写的存储库新的存储库。 Or at least, the new chain is new: the old stuff, from before the rewrite point, is the same. 至少,新链是新的:从重写点开始的旧内容是相同的。 You must get everyone who has copies of the old repository to switch from the old stuff to the new stuff. 您必须让拥有旧存储库副本的每个人都可以从内容切换到内容。 If even a single person retains the old commits, and uses Git to fuse the old with the new (which Git is quite happy to do), all the old commits come right back . 如果甚至只有一个人保留了旧提交,并使用Gi​​t将旧提交与新提交融合在一起(Git很高兴这样做),那么所有旧提交都会马上回来 Now you have the old and the new all squished together into one big happy (?) repository and your problems have just gotten worse! 现在,您将旧的和新的都压缩到一个大的快乐(?)存储库中,而您的问题变得越来越糟!

There is nothing fundamentally wrong with rewriting (some or all of) your commits into new history, you just need to (a) know how to do it and (b) be aware of this "everyone must switch" consequence. 将您的提交(部分或全部)重写为新的历史记录从根本上没有错,您只需要(a)知道如何做,以及(b)注意这种“每个人都必须切换”的后果。 If no one else has the commits that you're replacing, this kind of rewrite is completely safe—there's no other person or other Git repository that will bring the old commits back into play. 如果没有其他人拥有您要替换的提交,则这种重写是完全安全的-没有其他人其他Git存储库可以使旧提交重新发挥作用。 If everyone who does have the old commits understands how this works and plans to cooperate, this kind of history rewrite is mostly safe: it will go wrong only if someone makes a mistake (and then, depending on your own level of carefulness and/or paranoia, maybe only for them!). 如果确实有旧承诺的每个人都知道这是如何工作并计划进行合作的,则这种历史改写在大多数情况下都是安全的:只有在有人犯错的情况下它才会出错(然后,取决于您自己的谨慎和/或谨慎程度)偏执狂,也许只为他们!)。 It's when you have naive users, who just let Git's default Borg-like action of integrating every bit of technology it ever comes across, that this kind of rewrite makes a mess. 当您只有天真的用户时,他们只是让Git 像Borg这样的默认行为来集成所遇到的每一个技术,这种重写就变得一团糟。

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

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