如何从git仓库中删除作者?

16
如果我创建一个Git仓库并公开发布它(例如在GitHub等平台上),如果有贡献者请求删除或模糊化他们的姓名,是否有简单的方法可以实现?
基本上,我收到了这样的请求,可能想要用"匿名贡献者"或者他们电子邮件地址的SHA-1哈希值等方式替换他们的姓名和电子邮件地址。请问是否有相应的方法?

可能是更改特定提交的提交作者的重复问题。 - Cascabel
我选择的重复问题在问题中有一个指向另一个类似问题的链接 - 两个问题结合起来应该就可以了。 - Cascabel
注意:自Git 2.2(2014年11月)以来,git fast-export可以成为git filter-branch的有趣替代品:请参见下面的我的答案 - VonC
4个回答

11

Jeff说得很对,正确的方式是使用git filter-branch。它需要一个可以操作环境变量的脚本。对于您的用例,您可能需要像这样的东西:

git filter-branch --env-filter '
    if [ "$GIT_AUTHOR_NAME" = "Niko Schwarz" ]; then \
        export GIT_AUTHOR_NAME="Jon Doe" GIT_AUTHOR_EMAIL="john@bugmenot.com"; \
    fi
    '

你可以这样测试它是否有效:

$ cd /tmp
$ mkdir filter-branch && cd filter-branch
$ git init
Initialized empty Git repository in /private/tmp/filter-branch/.git/
$ 
$ touch hi && git add . && git commit -m bla
[master (root-commit) 081f7f5] bla
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 hi
$ echo howdi >> hi && git commit -a -m bla
[master a466a18] bla
 1 files changed, 1 insertions(+), 0 deletions(-)
$ git log
commit a466a18e4dc48908f7ba52f8a373dab49a6cfee4
Author: Niko Schwarz <niko.schwarz@gmail.com>
Date:   Thu Aug 12 09:43:44 2010 +0200

    bla

commit 081f7f50921edc703b55c04654218fe95d09dc3c
Author: Niko Schwarz <niko.schwarz@gmail.com>
Date:   Thu Aug 12 09:43:34 2010 +0200

    bla
$ 
$ git filter-branch --env-filter '
> if [ "$GIT_AUTHOR_NAME" = "Niko Schwarz" ]; then \    
> export GIT_AUTHOR_NAME="Jon Doe" GIT_AUTHOR_EMAIL="john@bugmenot.com"; \
> fi
> '
Rewrite a466a18e4dc48908f7ba52f8a373dab49a6cfee4 (2/2)
Ref 'refs/heads/master' was rewritten
$ git log
commit 5f0dfc0dc9a325a3f3aaf4575369f15b0fb21fe9
Author: Jon Doe <john@bugmenot.com>
Date:   Thu Aug 12 09:43:44 2010 +0200

    bla

commit 3cf865fa0a43d2343b4fb6c679c12fc23f7c6015
Author: Jon Doe <john@bugmenot.com>
Date:   Thu Aug 12 09:43:34 2010 +0200

    bla

请注意,无法删除作者姓名而不使所有后续提交哈希值失效。这将使后续合并对使用您存储库的人来说非常痛苦。


我对此进行了负投票,因为这是一个__危险__的命令。如果你在某个子分支上,它将循环遍历所有提交,__包括那些你从主分支上变基的提交__。 - VimNing
给我报了这个错误:1: eval: Syntax error: end of file unexpected (expecting "fi")。我通过将结尾的“\”替换为“;”来修复它。 - max

4
如果你需要对一个git仓库进行“匿名化”处理,不仅是针对一个用户,而是所有用户,那么Git 2.2(2014年11月)提供了一个有趣的功能,通过改进和增强的git fast-export实现:
请参见提交记录a872275提交记录75d3d65,作者为Jeff King (peff)
fast-export使用--anonymize选项:
有时用户想要报告他们在其存储库上遇到的错误,但不方便分享存储库的内容。
如果他们可以生成一个具有类似历史和树形结构的存储库,但不泄漏任何信息将很有用。
这个“匿名化”存储库随后可以与开发人员共享(假设它仍然复制了原始问题)。
这个补丁实现了一个“--anonymize”选项给fast-export,它生成一个可以重建这种存储库的流。
生成单一的流使调用者容易验证他们没有泄漏任何有用的信息。您可以运行像下面这样的命令来获取将要共享的概述:
git fast-export --anonymize --all |
perl -pe 's/\d+/X/g' |
sort -u |
less

这将显示我们生成的每一行唯一的内容,除了数字(每个匿名标记都被分配一个数字,比如“User 0”,并且我们在输出中保持一致地替换它)。
除了匿名化之外,这还会产生相对较小的测试用例(与原始存储库相比),并且生成速度快(与使用filter-branch或修改fast-export输出自己相比) 文档:
如果给出了--anonymize选项,git将尝试从存储库中删除所有识别信息,同时仍保留足够的原始树和历史模式以重现一些错误。
使用此选项,git将在输出中用匿名数据替换所有refnames、路径、blob内容、提交和标签消息、名称和电子邮件地址。同一字符串的两个实例将被等效地替换(例如,具有相同作者的两个提交将在输出中具有相同的匿名作者,但与原始作者字符串没有任何相似之处)。保留提交、分支和标记之间的关系以及提交时间戳(但提交消息和refnames与原始消息不相似)。保留树的相对结构(例如,如果您有一个根树,其中包含10个文件和3个树,则输出也将如此),但它们的名称和文件内容将被替换。

另请参阅Git 2.28(Q3 2020),"git fast-export --anonymize"学会了采用自定义映射,以允许其用户调整其输出,使其更易于进行调试。

请查看 提交 f39ad38, 提交 8a49495, 提交 65b5d9f (2020年6月25日),以及 提交 d5bf91f, 提交 6416a86, 提交 55b0145, 提交 a0f6564, 提交 7f40759, 提交 750bb32, 提交 b897bf5, 提交 b8c0689 (2020年6月23日),作者为 Jeff King (peff)
(由Junio C Hamano -- gitster --提交 0a23331 中合并)

fast-export: 允许种子化匿名映射

协助者: Eric Sunshine
签署者: Jeff King

在匿名化存储库之后,很难找到原始版本和结果之间对应的提交,因此很难重现在原始版本中触发错误的命令。让我们使得种子化匿名映射成为可能。这使得用户可以选择:标记要保留的名称(如果他们不认为它们是机密的,则其原始命令将正常工作),或者将名称映射到新值,这使他们能够适应新名称而不会泄露原始信息。实现相当简单。我们已经将每个匿名令牌存储在哈希表中(因此两次出现的相同令牌被转换为相同的结果)。我们只需引入一个新的“种子”哈希表,首先进行查询。这确实向用户承诺了更多关于我们将如何匿名化事物的内容(例如,拆分路径名的标记)。但即使单个令牌的匿名化实际上发生了变化,我们也不太可能想要更改那些规则。这使用户的操作变得更加容易,他们可以仅取消盲目处理目录名称,而无需指定其中的每个路径。这种方法的另一种替代方案是按照我们所看到的进行匿名化,然后将整个refname和pathname映射转储到文件中。这确实有效,但使用起来有点麻烦(您必须手动从映射中挖掘出您关心的项)。

git fast-export 现在有:

--anonymize-map=<from>[:<to>]

在匿名输出中将令牌 <from> 转换为 <to>
如果省略了 <to>,则将 <from> 映射到自身(即不进行匿名处理)。

Reproducing some bugs may require referencing particular commits or paths, which becomes challenging after refnames and paths have been anonymized.
You can ask for a particular token to be left as-is or mapped to a new value.

For example, if you have a bug which reproduces with git rev-list sensitive -- secret.c, you can run:

---------------------------------------------------
$ git fast-export --anonymize --all \
      --anonymize-map=sensitive:foo \
      --anonymize-map=secret.c:bar.c \
      >stream
---------------------------------------------------

After importing the stream, you can then run git rev-list foo -- bar.c in the anonymized repository.

Note that paths and refnames are split into tokens at slash boundaries.
The command above would anonymize subdir/secret.c as something like path123/bar.c; you could then search for bar.c in the anonymized repository to determine the final pathname.

To make referencing the final pathname simpler, you can map each path component; so if you also anonymize subdir to publicdir, then the final pathname would be publicdir/bar.c.


在Git 2.34之前(2021年第四季度),当使用其匿名化功能时, "git fast-export"(man) 的输出会错误地显示带注释的标签。

请查看提交2f040a9(2021年8月31日)作者为Tal Kelrich(hasturkun
(由Junio C Hamano -- gitster --提交febba80中合并,2021年9月10日)

fast-export:修复使用原始长度的匿名标签

签署者:Tal Kelrich

提交7f40759("fast-export: 收紧 anonymize_mem() 接口以仅处理字符串",2020年6月23日,Git v2.28.0-rc0 -- 合并 列在 批次 #7 中)更改了用于匿名化字符串的接口,但未更新注释标签消息的大小以匹配新的匿名化字符串。

因此,导出具有长度超过13个字符的消息的标签将创建无法被快速导入程序解析的输出,因为数据长度指示的比数据输出大。

在匿名化时重置消息大小,并向测试添加一个具有“长”消息的标签。


2
您可以在本地仓库中更改代码,使用git commit --amend修改适当的提交(即添加名称的提交),然后使用git push --force将您的版本更新到GitHub上。
原始提交包含贡献者的名称将仍然保留在reflog中(直到其过期为止),但要找回它需要付出很多努力。如果这是一个问题,您可以从reflog中彻底删除该特定提交-请参阅git help reflog以获取语法和如何在列表中找到它的信息。

这并不是一个真正的答案。git commit --amend 只能操作最后一次提交。你必须遍历所有过去的提交并修改它们。 - nes1983

1
如果您想更改多个提交,请查看手册页面。
git filter-branch --env-filter

你可以使用git-filter-branch来更改先前提交的内容/元数据。

请注意,由于您不处理本地分支(它已经被推送到github),因此您无法从已克隆您的分支的任何人中删除作者。

修改已发布的分支通常也是不好的做法,因为这可能会导致跟踪该分支的人感到困惑。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接