从 Git 历史记录中删除敏感文件及其提交

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/872565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 06:27:56  来源:igfitidea点击:

Remove sensitive files and their commits from Git history

gitgit-commitgit-filter-branchgit-rewrite-history

提问by Stefan

I would like to put a Git project on GitHub but it contains certain files with sensitive data (usernames and passwords, like /config/deploy.rb for capistrano).

我想在 GitHub 上放置一个 Git 项目,但它包含某些带有敏感数据(用户名和密码,例如 capistrano 的 /config/deploy.rb)的文件。

I know I can add these filenames to .gitignore, but this would not remove their history within Git.

我知道我可以将这些文件名添加到.gitignore,但这不会删除它们在 Git 中的历史记录。

I also don't want to start over again by deleting the /.git directory.

我也不想通过删除 /.git 目录重新开始。

Is there a way to remove alltraces of a particular file in your Git history?

有没有办法删除Git 历史记录中特定文件的所有痕迹?

回答by natacado

For all practical purposes, the firstthing you should be worried about is CHANGING YOUR PASSWORDS!It's not clear from your question whether your git repository is entirely local or whether you have a remote repository elsewhere yet; if it is remote and not secured from others you have a problem. If anyone has cloned that repository before you fix this, they'll have a copy of your passwords on their local machine, and there's no way you can force them to update to your "fixed" version with it gone from history. The only safe thing you can do is change your password to something else everywhere you've used it.

出于所有实际目的,您应该担心的第一件事是更改您的密码!从您的问题中不清楚您的 git 存储库是完全本地的还是其他地方是否有远程存储库;如果它是远程的并且不受其他人的保护,那么您就会遇到问题。如果有人在您修复此问题之前克隆了该存储库,他们将在其本地计算机上拥有您密码的副本,并且您无法强制他们更新到您的“已修复”版本,因为它已从历史记录中消失。您可以做的唯一安全的事情是将您的密码更改为您使用过的任何其他地方。



With that out of the way, here's how to fix it. GitHub answered exactly that question as an FAQ:

有了这个,这里是如何解决它。GitHub 在 FAQ 中准确地回答了这个问题

Note for Windows users: use double quotes (") instead of singles in this command

Windows 用户注意事项:在此命令中使用双引号 (") 而不是单引号

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

Update 2019:

2019 年更新:

This is the current code from the FAQ:

这是常见问题解答中的当前代码:

  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
  --prune-empty --tag-name-filter cat -- --all
  git push --force --verbose --dry-run
  git push --force

Keep in mind that once you've pushed this code to a remote repository like GitHub and others have cloned that remote repository, you're now in a situation where you're rewriting history. When others try pull down your latest changes after this, they'll get a message indicating that the changes can't be applied because it's not a fast-forward.

请记住,一旦您将此代码推送到 GitHub 等远程存储库并且其他人克隆了该远程存储库,您现在就处于重写历史记录的情况。当其他人在此之后尝试下拉您的最新更改时,他们会收到一条消息,指示无法应用更改,因为它不是快进。

To fix this, they'll have to either delete their existing repository and re-clone it, or follow the instructions under "RECOVERING FROM UPSTREAM REBASE" in the git-rebase manpage.

要解决此问题,他们必须删除现有存储库并重新克隆它,或者按照git-rebase 联机帮助页中“从上游重新数据库恢复”下的说明进行操作。

Tip: Execute git rebase --interactive

提示:执行git rebase --interactive



In the future, if you accidentally commit some changes with sensitive information but you notice beforepushing to a remote repository, there are some easier fixes. If you last commit is the one to add the sensitive information, you can simply remove the sensitive information, then run:

将来,如果您不小心提交了一些带有敏感信息的更改,但推送到远程存储库之前您注意到了,还有一些更简单的修复方法。如果你最后一次提交是添加敏感信息,你可以简单地删除敏感信息,然后运行:

git commit -a --amend

That will amend the previous commit with any new changes you've made, including entire file removals done with a git rm. If the changes are further back in history but still not pushed to a remote repository, you can do an interactive rebase:

这将使用您所做的任何新更改来修改之前的提交,包括使用git rm. 如果更改进一步追溯到历史但仍未推送到远程存储库,您可以执行交互式 rebase:

git rebase -i origin/master

That opens an editor with the commits you've made since your last common ancestor with the remote repository. Change "pick" to "edit" on any lines representing a commit with sensitive information, and save and quit. Git will walk through the changes, and leave you at a spot where you can:

这将打开一个编辑器,其中包含自您与远程存储库的最后一个共同祖先以来所做的提交。在任何代表带有敏感信息的提交的行上将“pick”更改为“edit”,然后保存并退出。Git 将完成更改,并将您留在一个地方,您可以:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

For each change with sensitive information. Eventually, you'll end up back on your branch, and you can safely push the new changes.

对于带有敏感信息的每个更改。最终,您将返回到您的分支,并且您可以安全地推送新的更改。

回答by Roberto Tyley

Changing your passwords is a good idea, but for the process of removing password's from your repo's history, I recommend the BFG Repo-Cleaner, a faster, simpler alternative to git-filter-branchexplicitly designed for removing private data from Git repos.

更改密码是一个好主意,但对于从存储库历史记录中删除密码的过程,我推荐使用BFG Repo-Cleaner,这是一种更快、更简单的替代方案,git-filter-branch专门用于从 Git 存储库中删除私有数据。

Create a private.txtfile listing the passwords, etc, that you want to remove (one entry per line) and then run this command:

创建一个private.txt文件,列出要删除的密码等(每行一个条目),然后运行以下命令:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

All files under a threshold size (1MB by default) in your repo's history will be scanned, and any matching string (that isn't in your latestcommit) will be replaced with the string "***REMOVED***". You can then use git gcto clean away the dead data:

将扫描存储库历史记录中低于阈值大小(默认为 1MB)的所有文件,并且任何匹配的字符串(不在您的最新提交中)将替换为字符串“***REMOVED***”。然后您可以使用git gc清除死数据:

$ git gc --prune=now --aggressive

The BFG is typically 10-50x faster than running git-filter-branchand the options are simplified and tailored around these two common use-cases:

BFG 通常比运行快 10-50 倍,git-filter-branch并且选项围绕以下两个常见用例进行了简化和定制:

  • Removing Crazy Big Files
  • Removing Passwords, Credentials& other Private data
  • 删除疯狂的大文件
  • 删除密码、凭据和其他私人数据

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

完全披露:我是 BFG Repo-Cleaner 的作者。

回答by Jason Goemaat

I recommend this scriptby David Underhill, worked like a charm for me.

我推荐大卫安德希尔的这个剧本,对我来说就像一个魅力。

It adds these commands in addition natacado's filter-branch to clean up the mess it leaves behind:

除了 natacado 的过滤器分支之外,它还添加了这些命令来清理它留下的混乱:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

Full script (all credit to David Underhill)

完整剧本(全部归功于大卫安德希尔)

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune

The last two commands may work better if changed to the following:

如果更改为以下最后两个命令可能会更好地工作:

git reflog expire --expire=now --all && \
git gc --aggressive --prune=now

回答by lostphilosopher

To be clear: The accepted answer is correct. Try it first. However, it may be unnecessarily complex for some use cases, particularly if you encounter obnoxious errors such as 'fatal: bad revision --prune-empty', or really don't care about the history of your repo.

需要明确的是:接受的答案是正确的。先试试。但是,对于某些用例,它可能会变得不必要地复杂,特别是如果您遇到令人讨厌的错误,例如“致命:错误修订 --prune-empty”,或者真的不关心您的存储库的历史记录。

An alternative would be:

另一种选择是:

  1. cd to project's base branch
  2. Remove the sensitive code / file
  3. rm -rf .git/ # Remove all git info from your code
  4. Go to github and delete your repository
  5. Follow this guide to push your code to a new repository as you normally would - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
  1. cd 到项目的基本分支
  2. 删除敏感代码/文件
  3. rm -rf .git/ # 从代码中删除所有 git 信息
  4. 转到 github 并删除您的存储库
  5. 按照本指南,像往常一样将代码推送到新的存储库 - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

This will of course remove all commit history branches, and issues from both your github repo, and your local git repo. If this is unacceptable you will have to use an alternate approach.

这当然会从你的 github 仓库和你的本地 git 仓库中删除所有提交历史分支和问题。如果这是不可接受的,您将不得不使用替代方法。

Call this the nuclear option.

称之为核选项。

回答by nachoparker

You can use git forget-blob.

您可以使用git forget-blob.

The usage is pretty simple git forget-blob file-to-forget. You can get more info here

用法很简单git forget-blob file-to-forget。你可以在这里获得更多信息

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

It will disappear from all the commits in your history, reflog, tags and so on

它将从您的历史记录、引用日志、标签等中的所有提交中消失

I run into the same problem every now and then, and everytime I have to come back to this post and others, that's why I automated the process.

我不时遇到同样的问题,每次我必须回到这篇文章和其他文章时,这就是我自动化流程的原因。

Credits to contributors from Stack Overflow that allowed me to put this together

感谢 Stack Overflow 的贡献者,让我把它们放在一起

回答by Shiv Krishna Jaiswal

Use filter-branch:

使用过滤器分支

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all

git push origin *branch_name* -f

回答by vertigo71

Here is my solution in windows

这是我在 Windows 中的解决方案

git filter-branch --tree-filter "rm -f 'filedir/filename'" HEAD

git push --force

git filter-branch --tree-filter "rm -f 'filedir/filename'" HEAD

git push --force

make sure that the path is correct otherwise it won't work

确保路径正确,否则将无法工作

I hope it helps

我希望它有帮助

回答by przbadu

So, It looks something like this:

所以,它看起来像这样:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

Remove cache for tracked file from git and add that file to .gitignorelist

从 git 中删除跟踪文件的缓存并将该文件添加到.gitignore列表中

回答by b01

I've had to do this a few times to-date. Note that this only works on 1 file at a time.

迄今为止,我不得不这样做了几次。请注意,这一次仅适用于 1 个文件。

  1. Get a list of all commits that modified a file. The one at the bottom will the the first commit:

    git log --pretty=oneline --branches -- pathToFile

  2. To remove the file from history use the first commit sha1 and the path to file from the previous command, and fill them into this command:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

  1. 获取修改文件的所有提交的列表。底部的将是第一次提交:

    git log --pretty=oneline --branches -- pathToFile

  2. 要从历史记录中删除文件,请使用第一个提交 sha1 和上一个命令中的文件路径,并将它们填充到此命令中:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

回答by Ercan

In my android project I had admob_keys.xmlas separated xml file in app/src/main/res/values/folder. To remove this sensitive file I used below script and worked perfectly.

在我的 android 项目中,我将admob_keys.xml作为分离的 xml 文件放在app/src/main/res/values/文件夹中。为了删除这个敏感文件,我使用了下面的脚本并且工作得很好。

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all