【问题标题】:Can I go back and remove particular files from previous git commits?我可以返回并从以前的 git 提交中删除特定文件吗?
【发布时间】:2021-07-05 00:25:35
【问题描述】:

我的项目中有 5 个文件。我已经完成了 10 次提交,并且在每次提交中,我修改并暂存了每个文件。是否可以将这些提交中的每一个更改为包含这 5 个文件中的第一个以外的所有文件?

【问题讨论】:

  • 很高兴,我花时间为你写了一个完整的答案。如果我的回答不可接受,请至少回复反馈。
  • @Inigo 我会尽快回复,我还没有查看。敬请期待:)

标签: git version-control


【解决方案1】:

是的,这是不可能的。我会给你四种方法来重写历史以从中删除文件...

⚠️ 如果出于安全原因需要从 git 历史记录中删除文件,则应按照Remove sensitive files and their commits from Git history 中的步骤操作。

交互式变基

如果你还不是 git pro,我建议你使用这种方法,即使有更多的手动步骤。

既然你想重写 10 个提交,你可以这样做:

git rebase -i HEAD~10

这将打开一个“待办事项列表”,列出从 HEAD~10 开始的提交,这将是最后 10 个提交。如果您将文件保持原样并关闭它,git rebase 将按照显示的顺序重播这 10 次提交,并且没有任何改变。但是通过编辑待办事项列表,您可以完全控制发生的事情。您可以重新排序任何行,从而重新排序提交。您可以删除一行或将pick 替换为skip 以跳过提交。或者您可以将“pick”替换为另一条指令。所有这些都在 todo 文件本身中进行了解释,因此您不必记住所有选项。

在您的情况下,您希望修改每个提交以删除一个文件。将每个pick 替换为edit 或只是e。关闭文件。然后 Git rebase 将重播这些提交,但会在每个提交后停止并允许您对其进行修改。

在第一个上,您将git rm 要删除的文件,然后git commit --amendnew 第一次提交将不再包含该文件。您现在可以继续下一个。

ℹ️ 在交互式 rebase 期间,您始终可以输入 git status,它会向您显示 rebase 的当前状态,并提示您可能接下来要做什么。 p>

继续下一个提交:git rebase --continue。 Rebase 将尝试重播下一次提交,但会因为冲突而停止:在您新重写的分支上,该文件不再存在,但在第二次重播时,该文件已被修改。

在这种情况下,您只需再次删除文件,然后git add . 以使用此选择的冲突解决方案更新暂存索引即可解决冲突。然后git rebase --continue 告诉 rebase 重试提交第二次提交,然后继续进行第三次提交。

ℹ️ 如果您认为自己犯了错误或改变主意,请运行git rebase --abort,一切都会恢复到以前的状态。

对于所有剩余的提交,同样的冲突将再次发生,您对所有提交执行相同的操作:

  • 删除文件
  • git add .记录你的决心
  • git rebase --continue 继续前进。

rebase 完成后,您当前的分支 HEAD 现在将指向一系列新的 10 次提交,而没有您想要删除的文件。原始提交仍然存在,但如果没有 refs 指向它们,它们最终会从 git 数据库中删除。

将 10 个提交压缩成一个没有文件的提交

如果您将 10 次提交压缩为 1 次,您可以跳过修改它们。如果您不需要历史记录来反映 10 次单独的编辑,如果您不关心历史记录是否只有一次提交以及所有组合更改,那么这是给你的票:

git reset HEAD~10

这会将当前分支的HEAD 移回 10 个提交。您在这 10 次提交中所做的所有更改仍将保留在工作树中,但不再提交到 git。您现在可以删除您不想要的文件和git add . 其余的文件,然后提交。 ,如果您不想删除该文件,只需将其保留在 git 之外,只需 git add <the other 9 files> 并提交即可。

git 过滤器分支

这是高级用户方式。它更加自动化。但我建议所有非 git-guru 都使用上面的git rebase,因为这是成为大师的好方法。

git docs for filter-branch 提供了从每次提交中删除文件的说明,因此请遵循这些说明。

git-filter-repo

我从未使用过git-filter-repo(还),但它非常推荐(显然是 git 维护者)。它本质上是一个对用户更友好的git filter-branch 版本。 @LeGEC 在评论中提供了特定命令,所以我在这里添加它:

git filter-repo --invert-paths --path file/to/ignore

祝你好运!

【讨论】:

  • 添加到@Inigo 的答案:使用git filter-repo,尝试:git filter-repo --invert-paths --path file/to/ignore
  • @LeGEC,你认为我为学习 git 的人推荐更手动的路径,交互式 rebase 是否离谱?
  • 不,我不知道,它是一个非常有用且用途广泛的工具,绝对值得一提。但是,对于这个问题的特定需求(“从历史记录中删除一个文件”),有一个解决方案(使用git filter-repogit filter-branch)。所以它们也值得一提。
  • 我将您提供的filter-repo 命令添加到答案中。谢谢!
  • 我在您的回答中将 HEAD 大写,因为小写 head 仅在特定情况下有效(例如,在 Windows 和 macOS 的默认大小写折叠文件系统中)。还值得一提的是,这不会改变 original 提交:它会进行新的和改进的 replacement 提交,我们现在希望每个人都使用这些提交而不是原始提交。
猜你喜欢
  • 1970-01-01
  • 2021-02-24
  • 2012-07-29
  • 2018-12-05
  • 2018-12-07
  • 2012-04-29
  • 2010-10-09
相关资源
最近更新 更多