如何修复错误的合并,并将良好的提交重放到固定的合并上?

发布于 2024-07-09 02:50:46 字数 157 浏览 10 评论 0原文

我在几次提交前不小心将不需要的文件(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

悲欢浪云 2024-07-16 02:50:46

如果您的情况与问题中描述的情况不同,请不要使用此食谱。 这个配方是为了修复错误的合并,并将你的好的提交重播到固定的合并上。

虽然 filter-branch 会做你想要的,但这是一个相当复杂的命令,我可能会选择使用 git rebase 来执行此操作。 这可能是个人喜好。 filter-branch 可以通过一个稍微复杂的命令来完成此操作,而 rebase 解决方案一次执行一步等效的逻辑操作。

尝试以下方法:(

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

请注意,您实际上不需要临时分支,您可以使用“分离的 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 with git rebase. It's probably a personal preference. filter-branch can do it in a single, slightly more complex command, whereas the rebase solution is performing the equivalent logical operations one step at a time.

Try the following recipe:

# create and check out a temporary branch at the location of the bad merge
git checkout -b tmpfix <sha1-of-merge>

# remove the incorrectly added file
git rm somefile.orig

# commit the amended merge
git commit --amend

# go back to the master branch
git checkout master

# replant the master branch onto the corrected merge
git rebase tmpfix

# delete the temporary branch
git branch -d tmpfix

(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 the git rebase command rather than using the temporary branch name.)

喜爱皱眉﹌ 2024-07-16 02:50:46

简介:您有 5 个可用的解决方案

原海报指出:

我不小心向我的存储库提交了一个不需要的文件...多次提交
之前...我想从存储库历史记录中完全删除该文件。

是吗
可以重写更改历史记录,使得 filename.orig 永远不会
首先添加到存储库中吗?

有许多不同的方法可以完全删除文件的历史记录
git:

  1. 修改提交。
  2. 硬重置(可能加上变基)。
  3. 非交互式变基。
  4. 交互式变基。
  5. 过滤分支。

对于原始海报来说,修改提交并不是真正的选择
本身,因为他后来做了几次额外的承诺,但为了
为了完整性,我还将向任何其他人解释如何做到这一点
想要修改他们之前的提交。

请注意,所有这些解决方案都涉及更改/重写历史/提交
以一种方式另一种方式,所以任何拥有旧提交副本的人都必须这样做
额外的工作来将其历史记录与新历史记录重新同步。


解决方案 1:修改提交

如果您不小心在以前的历史记录中进行了更改(例如添加文件)
提交,并且您不希望该更改的历史记录再存在,那么
您可以简单地修改先前的提交以从中删除文件:

git rm <file>
git commit --amend --no-edit

解决方案 2:硬重置(可能加上 Rebase)

与解决方案 #1 类似,如果您只想删除先前的提交,那么您
还可以选择简单地对其父级进行硬重置:

git reset --hard HEAD^

该命令会将您的分支硬重置到前一个st父级
犯罪。

但是,如果像原始发布者一样,您在之后进行了多次提交
您想要撤消更改的提交,您仍然可以使用硬重置
修改它,但这样做还涉及使用变基。 以下是步骤
您可以使用它来修改历史记录中的提交:

# Create a new branch at the commit you want to amend
git checkout -b temp <commit>

# Amend the commit
git rm <file>
git commit --amend --no-edit

# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --rebase-merges --onto temp <old-commit> master

# Verify your changes
git diff master@{1}

解决方案 3:非交互式变基

如果您只想从历史记录中完全删除提交,那么这将起作用:

# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>

# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --rebase-merges --onto temp <commit-to-remove> master

# Or use `-r` instead of the longer `--rebase-merges`
git rebase -r --onto temp <commit-to-remove> master

# Verify your changes
git diff master@{1}

解决方案 4:交互式变基

该解决方案将允许您完成与以下相同的事情:解决方案#2 和
#3,即修改或删除历史记录中比您当前更早的提交
之前的提交,因此您选择使用哪种解决方案取决于您。
交互式变基不太适合对数百个提交进行变基,因为
性能原因,所以我会使用非交互式变基或过滤器分支
在这种情况下的解决方案(见下文)。

要开始交互式变基,请使用以下命令:

git rebase --interactive <commit-to-amend-or-remove>~

# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~

这将导致 git 将提交历史记录回退到父级
提交您想要修改或删除的内容。 然后它会向您呈现一个列表
在 git 设置使用的任何编辑器中以相反的顺序回滚提交(这是
Vim 默认情况下):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

您要修改或删除的提交将位于此列表的顶部。
要删除它,只需删除列表中的行即可。 否则,将“pick”替换为
在第一行中输入“edit”,如下所示:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

接下来,输入 git rebase --continue。 如果您选择完全删除提交,
然后您需要做的就是(除了验证之外,请参阅最后一步)
这个解决方案)。 另一方面,如果您想修改提交,那么 git
将重新应用提交,然后暂停变基。

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

此时,您可以删除该文件并修改提交,然后继续
变基:

git rm <file>
git commit --amend --no-edit
git rebase --continue

就是这样。 作为最后一步,无论您修改提交还是删除它
完全正确,验证没有其他意外变化总是一个好主意

git diff master@{1}

解决方案 5:过滤分支

最后,如果您想完全清除所有痕迹,则此解决方案是最好
文件的存在历史记录,其他解决方案都不能完全满足要求
任务。

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'

这将从根提交开始从所有提交中删除 。 如果
相反,你只想重写提交范围 HEAD~5..HEAD,那么你可以
将其作为附加参数传递给 filter-branch,如中指出的
这个答案

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD

同样,在 filter-branch 完成后,通常是一个好主意核实
通过将您的分支与它的分支进行比较,不会有其他意外的变化
过滤操作之前的先前状态:

git diff master@{1}

Filter-Branch 替代方案:BFG Repo Cleaner

我听说 BFG Repo Cleaner 工具运行比 git filter-branch 更快,因此您可能也想将其作为一个选项进行检查。 它甚至在 filter-branch 文档中正式提到作为可行的替代方案:

git-filter-branch 允许您进行复杂的 shell 脚本重写
你的 Git 历史记录,但如果你可能不需要这种灵活性
您只是删除不需要的数据,例如大文件或密码。
对于这些操作,您可能需要考虑 BFG
Repo-Cleaner
,一个基于 JVM 的
git-filter-branch 的替代方案,通常至少快 10-50 倍
这些用例,并且具有完全不同的特征:

  • 文件的任何特定版本都会被清理一次。 BFG 与 git-filter-branch 不同,它不给你机会处理
    文件根据提交的地点或时间而有所不同
    历史。 此约束提供了核心性能优势
    BFG,非常适合清理坏数据的任务 - 你不需要
    关心坏数据在哪里,您只想让它消失。

  • 默认情况下,BFG 充分利用多核机器,并行清理提交文件树。 git-filter-branch 清理
    顺序提交(即以单线程方式),尽管它
    可以编写包含自己的并行性的过滤器,在
    针对每次提交执行的脚本。

  • 命令选项很多
    比 git-filter 分支更具限制性,并且仅致力于
    删除不需要的数据的任务 - 例如:--strip-blobs-bigger-than 1M

其他资源

  1. Pro Git § 6.4 Git 工具 - 重写历史记录
  2. git-filter-branch(1) 手册页
  3. git-commit(1) 手册页
  4. git-reset(1) 手册页
  5. git-rebase(1) 手册页
  6. BFG Repo Cleaner(另请参阅这个答案来自创作者本人)。

Intro: You Have 5 Solutions Available

The original poster states:

I accidentally committed an unwanted file...to my repository several commits
ago...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?

There are many different ways to remove the history of a file completely from
git:

  1. Amending commits.
  2. Hard resets (possibly plus a rebase).
  3. Non-interactive rebase.
  4. Interactive rebases.
  5. Filtering branches.

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:

git rm <file>
git commit --amend --no-edit

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:

git reset --hard HEAD^

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:

# Create a new branch at the commit you want to amend
git checkout -b temp <commit>

# Amend the commit
git rm <file>
git commit --amend --no-edit

# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --rebase-merges --onto temp <old-commit> master

# Verify your changes
git diff master@{1}

Solution 3: Non-interactive Rebase

This will work if you just want to remove a commit from history entirely:

# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>

# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --rebase-merges --onto temp <commit-to-remove> master

# Or use `-r` instead of the longer `--rebase-merges`
git rebase -r --onto temp <commit-to-remove> master

# Verify your changes
git diff master@{1}

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:

git rebase --interactive <commit-to-amend-or-remove>~

# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~

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):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

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:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

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.

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

At this point, you can remove the file and amend the commit, then continue the
rebase:

git rm <file>
git commit --amend --no-edit
git rebase --continue

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:

git diff master@{1}

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.

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'

That will remove <file> from all commits, starting from the root commit. If
instead you just want to rewrite the commit range HEAD~5..HEAD, then you can
pass that as an additional argument to filter-branch, as pointed out in
this answer:

git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD

Again, after the filter-branch is complete, it's usually a good idea to verify
that there are no other unexpected changes by diffing your branch with its
previous state before the filtering operation:

git diff master@{1}

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:

git-filter-branch allows you to make complex shell-scripted rewrites
of your Git history, but you probably don’t need this flexibility if
you’re simply removing unwanted data like large files or passwords.
For those operations you may want to consider The BFG
Repo-Cleaner
, a JVM-based
alternative to git-filter-branch, typically at least 10-50x faster for
those use-cases, and with quite different characteristics:

  • Any particular version of a file is cleaned exactly once. The BFG, unlike git-filter-branch, does not give you the opportunity to handle
    a file differently based on where or when it was committed within your
    history. This constraint gives the core performance benefit of The
    BFG, and is well-suited to the task of cleansing bad data - you don’t
    care where the bad data is, you just want it gone.

  • By default The BFG takes full advantage of multi-core machines, cleansing commit file-trees in parallel. git-filter-branch cleans
    commits sequentially (ie in a single-threaded manner), though it is
    possible to write filters that include their own parallellism, in the
    scripts executed against each commit.

  • The command options are much
    more restrictive than git-filter branch, and dedicated just to the
    tasks of removing unwanted data- e.g: --strip-blobs-bigger-than 1M.

Additional Resources

  1. Pro Git § 6.4 Git Tools - Rewriting History.
  2. git-filter-branch(1) Manual Page.
  3. git-commit(1) Manual Page.
  4. git-reset(1) Manual Page.
  5. git-rebase(1) Manual Page.
  6. The BFG Repo Cleaner (see also this answer from the creator himself).
白龙吟 2024-07-16 02:50:46

如果此后您没有提交任何内容,只需 git rm 文件和 git commit --amend 即可。

如果您要

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

经历从 merge-pointHEAD 的每次更改,请删除 filename.orig 并重写更改。 使用 --ignore-unmatch 意味着如果由于某种原因 filename.orig 在更改中丢失,该命令不会失败。 这是 git-filter-branch 手册页 中示例部分的推荐方法。

Windows 用户注意:文件路径必须使用正斜杠

If you haven't committed anything since, just git rm the file and git commit --amend.

If you have

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

will go through each change from merge-point to HEAD, 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

夏末染殇 2024-07-16 02:50:46

这是最好的方法:
http://github.com/guides/completely-remove-a -file-from-all-revisions

请务必先备份文件的副本。

编辑

Neon 的编辑在审核过程中不幸被拒绝。
请参阅下面的 Neons 帖子,它可能包含有用的信息!


例如,删除所有意外提交到 git 存储库的 *.gz 文件:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

这对我来说仍然不起作用吗? (我目前的 git 版本为 1.7.6.1)

$ du -sh .git ==> e.g. 100M

不知道为什么,因为我只有一个主分支。 无论如何,我终于通过推入一个新的空的、裸露的 git 存储库来真正清理了我的 git 存储库,例如

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(是的!)

然后我将其克隆到一个新目录并将其 .git 文件夹移动到这个。 例如

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(是的!终于清理干净了!)

验证一切正常后,您可以删除../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:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

That still didn't work for me? (I am currently at git version 1.7.6.1)

$ du -sh .git ==> e.g. 100M

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.

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(yes!)

Then I clone that to a new directory and moved over it's .git folder into this one. e.g.

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(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...)

债姬 2024-07-16 02:50:46

重写 Git 历史记录需要更改所有受影响的提交 ID,因此参与该项目的每个人都需要删除其旧的存储库副本,并在清理历史记录后进行新的克隆。 它给越多的人带来不便,你就越需要一个充分的理由来这样做 - 你多余的文件并不会真正造成问题,但如果只有正在处理这个项目,你最好清理一下如果你愿意的话,可以查看 Git 历史记录!

为了使其尽可能简单,我建议使用 BFG Repo-Cleaner ,一个更简单、更快速的 git-filter-branch 替代方案,专为从 Git 历史记录中删除文件而设计。 它让您的生活更轻松的一种方式是,它实际上默认处理所有引用(所有标签、分支等),但它也是10 - 50 倍 快。

您应该仔细按照此处的步骤操作:http://rtyley.github.com/bfg- repo-cleaner/#usage - 但核心就是这样:下载 BFG jar(需要 Java 6 或更高版本)并运行此命令:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

将扫描您的整个存储库历史记录,以及任何名为 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:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

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 using git-filter-branch to do the same thing!

Full disclosure: I'm the author of the BFG Repo-Cleaner.

半边脸i 2024-07-16 02:50:46

您可能应该首先克隆您的存储库。

从所有分支历史记录中删除您的文件:

git filter-branch --tree-filter 'rm -f filename.orig' -- --all

仅从当前分支中删除您的文件:

git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

最后您应该运行以删除空提交:

git filter-branch -f --prune-empty -- --all

You should probably clone your repository first.

Remove your file from all branches history:

git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:

git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:

git filter-branch -f --prune-empty -- --all
秋叶绚丽 2024-07-16 02:50:46

只是为了将其添加到 Charles Bailey 的解决方案中,我只是使用 git rebase -i 从早期提交中删除不需要的文件,它的工作就像一个魅力。
步骤:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

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:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue
悲欢浪云 2024-07-16 02:50:46

我发现的最简单的方法是由 leontalbot (作为评论)建议的,这是一个 Anoopjohn 发布的帖子。 我认为它值得拥有自己的空间作为答案:(

我将其转换为 bash 脚本)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

所有积分均归于 Annopjohnleontalbot 指出。

注意

请注意,该脚本不包含验证,因此请确保您不会犯错误,并且您有备份以防出现问题。 它对我有用,但在你的情况下可能不起作用。 请谨慎使用(如果您想知道发生了什么,请点击链接)。

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)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

All credits goes to Annopjohn, and to leontalbot 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).

沫离伤花 2024-07-16 02:50:46

当然,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

南渊 2024-07-16 02:50:46

如果这是您想要清理的最新提交,我尝试使用 git 版本 2.14.3 (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

If it's the latest commit you want to clean up, I tried with git version 2.14.3 (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git
春花秋月 2024-07-16 02:50:46

这就是 git filter-branch 的设计为了。

This is what git filter-branch was designed for.

谜泪 2024-07-16 02:50:46

您还可以使用:

git重置HEAD文件/路径

You can also use:

git reset HEAD file/path

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文