如何修复错误的合并,并将良好的提交重放到固定的合并上?
我在几次提交前不小心将不需要的文件(filename.orig
)提交到了我的存储库,直到现在我才注意到。 我想从存储库历史记录中完全删除该文件。
是否可以重写更改历史记录,使 filename.orig
从未添加到存储库中?
I accidentally committed an unwanted file (filename.orig
while resolving a merge) to my repository several commits ago, without me noticing it until now. I want to completely delete the file from the repository history.
Is it possible to rewrite the change history such that filename.orig
was never added to the repository in the first place?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
如果您的情况与问题中描述的情况不同,请不要使用此食谱。 这个配方是为了修复错误的合并,并将你的好的提交重播到固定的合并上。
虽然
filter-branch
会做你想要的,但这是一个相当复杂的命令,我可能会选择使用 git rebase 来执行此操作。 这可能是个人喜好。filter-branch
可以通过一个稍微复杂的命令来完成此操作,而rebase
解决方案一次执行一步等效的逻辑操作。尝试以下方法:(
请注意,您实际上不需要临时分支,您可以使用“分离的 HEAD”来执行此操作,但您需要记下 git 提交生成的提交 ID --修改步骤以提供给
git rebase
命令,而不是使用临时分支名称。)Please don't use this recipe if your situation is not the one described in the question. This recipe is for fixing a bad merge, and replaying your good commits onto a fixed merge.
Although
filter-branch
will do what you want, it is quite a complex command and I would probably choose to do this withgit rebase
. It's probably a personal preference.filter-branch
can do it in a single, slightly more complex command, whereas therebase
solution is performing the equivalent logical operations one step at a time.Try the following recipe:
(Note that you don't actually need a temporary branch, you can do this with a 'detached HEAD', but you need to take a note of the commit id generated by the
git commit --amend
step to supply to thegit rebase
command rather than using the temporary branch name.)简介:您有 5 个可用的解决方案
原海报指出:
有许多不同的方法可以完全删除文件的历史记录
git:
对于原始海报来说,修改提交并不是真正的选择
本身,因为他后来做了几次额外的承诺,但为了
为了完整性,我还将向任何其他人解释如何做到这一点
想要修改他们之前的提交。
请注意,所有这些解决方案都涉及更改/重写历史/提交
以一种方式另一种方式,所以任何拥有旧提交副本的人都必须这样做
额外的工作来将其历史记录与新历史记录重新同步。
解决方案 1:修改提交
如果您不小心在以前的历史记录中进行了更改(例如添加文件)
提交,并且您不希望该更改的历史记录再存在,那么
您可以简单地修改先前的提交以从中删除文件:
解决方案 2:硬重置(可能加上 Rebase)
与解决方案 #1 类似,如果您只想删除先前的提交,那么您
还可以选择简单地对其父级进行硬重置:
该命令会将您的分支硬重置到前一个st父级
犯罪。
但是,如果像原始发布者一样,您在之后进行了多次提交
您想要撤消更改的提交,您仍然可以使用硬重置
修改它,但这样做还涉及使用变基。 以下是步骤
您可以使用它来修改历史记录中的提交:
解决方案 3:非交互式变基
如果您只想从历史记录中完全删除提交,那么这将起作用:
解决方案 4:交互式变基
该解决方案将允许您完成与以下相同的事情:解决方案#2 和
#3,即修改或删除历史记录中比您当前更早的提交
之前的提交,因此您选择使用哪种解决方案取决于您。
交互式变基不太适合对数百个提交进行变基,因为
性能原因,所以我会使用非交互式变基或过滤器分支
在这种情况下的解决方案(见下文)。
要开始交互式变基,请使用以下命令:
这将导致 git 将提交历史记录回退到父级
提交您想要修改或删除的内容。 然后它会向您呈现一个列表
在 git 设置使用的任何编辑器中以相反的顺序回滚提交(这是
Vim 默认情况下):
您要修改或删除的提交将位于此列表的顶部。
要删除它,只需删除列表中的行即可。 否则,将“pick”替换为
在第一行中输入“edit”,如下所示:
接下来,输入 git rebase --continue。 如果您选择完全删除提交,
然后您需要做的就是(除了验证之外,请参阅最后一步)
这个解决方案)。 另一方面,如果您想修改提交,那么 git
将重新应用提交,然后暂停变基。
此时,您可以删除该文件并修改提交,然后继续
变基:
就是这样。 作为最后一步,无论您修改提交还是删除它
完全正确,验证没有其他意外变化总是一个好主意
的
解决方案 5:过滤分支
最后,如果您想完全清除所有痕迹,则此解决方案是最好
文件的存在历史记录,其他解决方案都不能完全满足要求
任务。
这将从根提交开始从所有提交中删除
。 如果相反,你只想重写提交范围
HEAD~5..HEAD
,那么你可以将其作为附加参数传递给
filter-branch
,如中指出的这个答案:
同样,在
filter-branch
完成后,通常是一个好主意核实通过将您的分支与它的分支进行比较,不会有其他意外的变化
过滤操作之前的先前状态:
Filter-Branch 替代方案:BFG Repo Cleaner
我听说 BFG Repo Cleaner 工具运行比 git filter-branch 更快,因此您可能也想将其作为一个选项进行检查。 它甚至在 filter-branch 文档中正式提到作为可行的替代方案:
其他资源
Intro: You Have 5 Solutions Available
The original poster states:
There are many different ways to remove the history of a file completely from
git:
In the case of the original poster, amending the commit isn't really an option
by itself, since he made several additional commits afterwards, but for the sake
of completeness, I will also explain how to do it, for anyone else who justs
wants to amend their previous commit.
Note that all of these solutions involve altering/re-writing history/commits
in one way another, so anyone with old copies of the commits will have to do
extra work to re-sync their history with the new history.
Solution 1: Amending Commits
If you accidentally made a change (such as adding a file) in your previous
commit, and you don't want the history of that change to exist anymore, then
you can simply amend the previous commit to remove the file from it:
Solution 2: Hard Reset (Possibly Plus a Rebase)
Like solution #1, if you just want to get rid of your previous commit, then you
also have the option of simply doing a hard reset to its parent:
That command will hard-reset your branch to the previous 1st parent
commit.
However, if, like the original poster, you've made several commits after
the commit you want to undo the change to, you can still use hard resets to
modify it, but doing so also involves using a rebase. Here are the steps that
you can use to amend a commit further back in history:
Solution 3: Non-interactive Rebase
This will work if you just want to remove a commit from history entirely:
Solution 4: Interactive Rebases
This solution will allow you to accomplish the same things as solutions #2 and
#3, i.e. modify or remove commits further back in history than your immediately
previous commit, so which solution you choose to use is sort of up to you.
Interactive rebases are not well-suited to rebasing hundreds of commits, for
performance reasons, so I would use non-interactive rebases or the filter branch
solution (see below) in those sort of situations.
To begin the interactive rebase, use the following:
This will cause git to rewind the commit history back to the parent of the
commit that you want to modify or remove. It will then present you a list of the
rewound commits in reverse order in whatever editor git is set to use (this is
Vim by default):
The commit that you want to modify or remove will be at the top of this list.
To remove it, simply delete its line in the list. Otherwise, replace "pick" with
"edit" on the 1st line, like so:
Next, enter
git rebase --continue
. If you chose to remove the commit entirely,then that it all you need to do (other than verification, see final step for
this solution). If, on the other hand, you wanted to modify the commit, then git
will reapply the commit and then pause the rebase.
At this point, you can remove the file and amend the commit, then continue the
rebase:
That's it. As a final step, whether you modified the commit or removed it
completely, it's always a good idea to verify that no other unexpected changes
were made to your branch by diffing it with its state before the rebase:
Solution 5: Filtering Branches
Finally, this solution is best if you want to completely wipe out all traces of
a file's existence from history, and none of the other solutions are quite up to
the task.
That will remove
<file>
from all commits, starting from the root commit. Ifinstead you just want to rewrite the commit range
HEAD~5..HEAD
, then you canpass that as an additional argument to
filter-branch
, as pointed out inthis answer:
Again, after the
filter-branch
is complete, it's usually a good idea to verifythat there are no other unexpected changes by diffing your branch with its
previous state before the filtering operation:
Filter-Branch Alternative: BFG Repo Cleaner
I've heard that the BFG Repo Cleaner tool runs faster than
git filter-branch
, so you might want to check that out as an option too. It's even mentioned officially in the filter-branch documentation as a viable alternative:Additional Resources
如果此后您没有提交任何内容,只需
git rm
文件和git commit --amend
即可。如果您要
经历从
merge-point
到HEAD
的每次更改,请删除 filename.orig 并重写更改。 使用--ignore-unmatch
意味着如果由于某种原因 filename.orig 在更改中丢失,该命令不会失败。 这是 git-filter-branch 手册页 中示例部分的推荐方法。Windows 用户注意:文件路径必须使用正斜杠
If you haven't committed anything since, just
git rm
the file andgit commit --amend
.If you have
will go through each change from
merge-point
toHEAD
, delete filename.orig and rewrite the change. Using--ignore-unmatch
means the command won't fail if for some reason filename.orig is missing from a change. That's the recommended way from the Examples section in the git-filter-branch man page.Note for Windows users: The file path must use forward slashes
这是最好的方法:
http://github.com/guides/completely-remove-a -file-from-all-revisions
请务必先备份文件的副本。
编辑
Neon 的编辑在审核过程中不幸被拒绝。
请参阅下面的 Neons 帖子,它可能包含有用的信息!
例如,删除所有意外提交到 git 存储库的
*.gz
文件:这对我来说仍然不起作用吗? (我目前的 git 版本为 1.7.6.1)
不知道为什么,因为我只有一个主分支。 无论如何,我终于通过推入一个新的空的、裸露的 git 存储库来真正清理了我的 git 存储库,例如
(是的!)
然后我将其克隆到一个新目录并将其 .git 文件夹移动到这个。 例如
(是的!终于清理干净了!)
验证一切正常后,您可以删除
../large_dot_git
和../tmpdir
目录(也许在几周或几个月后,以防万一......)This is the best way:
http://github.com/guides/completely-remove-a-file-from-all-revisions
Just be sure to backup the copies of the files first.
EDIT
The edit by Neon got unfortunately rejected during review.
See Neons post below, it might contain useful information!
E.g. to remove all
*.gz
files accidentally committed into git repository:That still didn't work for me? (I am currently at git version 1.7.6.1)
Not sure why, since I only had ONE master branch. Anyways, I finally got my git repo truely cleaned up by pushing into a new empty and bare git repository, e.g.
(yes!)
Then I clone that to a new directory and moved over it's .git folder into this one. e.g.
(yeah! finally cleaned up!)
After verifying that all is well, then you can delete the
../large_dot_git
and../tmpdir
directories (maybe in a couple weeks or month from now, just in case...)重写 Git 历史记录需要更改所有受影响的提交 ID,因此参与该项目的每个人都需要删除其旧的存储库副本,并在清理历史记录后进行新的克隆。 它给越多的人带来不便,你就越需要一个充分的理由来这样做 - 你多余的文件并不会真正造成问题,但如果只有你正在处理这个项目,你最好清理一下如果你愿意的话,可以查看 Git 历史记录!
为了使其尽可能简单,我建议使用 BFG Repo-Cleaner ,一个更简单、更快速的 git-filter-branch 替代方案,专为从 Git 历史记录中删除文件而设计。 它让您的生活更轻松的一种方式是,它实际上默认处理所有引用(所有标签、分支等),但它也是10 - 50 倍 快。
您应该仔细按照此处的步骤操作:http://rtyley.github.com/bfg- repo-cleaner/#usage - 但核心就是这样:下载 BFG jar(需要 Java 6 或更高版本)并运行此命令:
将扫描您的整个存储库历史记录,以及任何名为
filename.orig
的文件(不在您的 最新提交)将被删除。 这比使用 git-filter-branch 做同样的事情要容易得多!全面披露:我是 BFG Repo-Cleaner 的作者。
Rewriting Git history demands changing all the affected commit ids, and so everyone who's working on the project will need to delete their old copies of the repo, and do a fresh clone after you've cleaned the history. The more people it inconveniences, the more you need a good reason to do it - your superfluous file isn't really causing a problem, but if only you are working on the project, you might as well clean up the Git history if you want to!
To make it as easy as possible, I'd recommend using the BFG Repo-Cleaner, a simpler, faster alternative to
git-filter-branch
specifically designed for removing files from Git history. One way in which it makes your life easier here is that it actually handles all refs by default (all tags, branches, etc) but it's also 10 - 50x faster.You should carefully follow the steps here: http://rtyley.github.com/bfg-repo-cleaner/#usage - but the core bit is just this: download the BFG jar (requires Java 6 or above) and run this command:
Your entire repository history will be scanned, and any file named
filename.orig
(that's not in your latest commit) will be removed. This is considerably easier than usinggit-filter-branch
to do the same thing!Full disclosure: I'm the author of the BFG Repo-Cleaner.
您可能应该首先克隆您的存储库。
从所有分支历史记录中删除您的文件:
仅从当前分支中删除您的文件:
最后您应该运行以删除空提交:
You should probably clone your repository first.
Remove your file from all branches history:
Remove your file just from the current branch:
Lastly you should run to remove empty commits:
只是为了将其添加到 Charles Bailey 的解决方案中,我只是使用 git rebase -i 从早期提交中删除不需要的文件,它的工作就像一个魅力。
步骤:
Just to add that to Charles Bailey's solution, I just used a git rebase -i to remove unwanted files from an earlier commit and it worked like a charm.
The steps:
我发现的最简单的方法是由
leontalbot
(作为评论)建议的,这是一个 Anoopjohn 发布的帖子。 我认为它值得拥有自己的空间作为答案:(我将其转换为 bash 脚本)
所有积分均归于
Annopjohn
和leontalbot
指出。注意
请注意,该脚本不包含验证,因此请确保您不会犯错误,并且您有备份以防出现问题。 它对我有用,但在你的情况下可能不起作用。 请谨慎使用(如果您想知道发生了什么,请点击链接)。
The simplest way I found was suggested by
leontalbot
(as a comment), which is a post published by Anoopjohn. I think its worth its own space as an answer:(I converted it to a bash script)
All credits goes to
Annopjohn
, and toleontalbot
for pointing it out.NOTE
Be aware that the script doesn't include validations, so be sure you don't make mistakes and that you have a backup in case something goes wrong. It worked for me, but it may not work in your situation. USE IT WITH CAUTION (follow the link if you want to know what is going on).
当然,
git filter-branch
是正确的选择。遗憾的是,这不足以从您的存储库中完全删除
filename.orig
,因为它仍然可以被标签、引用日志条目、遥控器等引用。我建议也删除所有这些引用,然后调用垃圾收集器。 您可以使用 这个 网站一步完成这一切。
git忘记blob文件名.orig
Definitely,
git filter-branch
is the way to go.Sadly, this will not suffice to completely remove
filename.orig
from your repo, as it can be still be referenced by tags, reflog entries, remotes and so on.I recommend removing all these references as well, and then calling the garbage collector. You can use the
git forget-blob
script from this website to do all this in one step.git forget-blob filename.orig
如果这是您想要清理的最新提交,我尝试使用 git 版本 2.14.3 (Apple Git-98):
If it's the latest commit you want to clean up, I tried with git version 2.14.3 (Apple Git-98):
这就是
git filter-branch
的设计为了。This is what
git filter-branch
was designed for.您还可以使用:
git重置HEAD文件/路径
You can also use:
git reset HEAD file/path