Git "rebase"但忽略新的.gitignore文件中的文件

3
有没有一种方法可以将源分支中的提交重放到目标分支中,但同时考虑目标分支中 .gitignore 的更改?
下面是场景:
假设我从主分支创建了一个新分支并开始提交文件,其中包括应该在 .gitignore 中但未被忽略的 .bin 文件。是否有任何方法可以回到主分支,将 "*.bin" 提交到 .gitignore 中,然后在 rebase 时修复我的主题分支(或其他自动操作)?通过修复,我的意思是删除任何 .bin 文件的变更集,因为它们现在被忽略了。这意味着:1)如果提交对 a.txtfoo.bin 进行了更改,则只提交 a.txt;2)如果提交只对 foo.bin 进行了更改,则应完全删除该提交。
目标是轻松清理 Pull Request 时间才发现的多个错误。
常规的 git rebase 无法解决问题。即使在仓库的(新的)线性历史记录中,gitignore 模式出现在错误提交之前,错误提交的文件仍然存在。

这与gitignore的内容无关。要更正错误,请进行一次纠正错误的提交。 - matt
2
@matt 进行一次提交来纠正错误,并不能清理存储库历史中仍存在的大文件。采用这种简单的方法,那么使用 --amend 的意义何在?只需提交更正内容,而不必修改。在这种情况下,我需要重写历史记录。 - Hilikus
然后重写历史记录。但正如我所说,这与gitignore无关。请阅读https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository。 - matt
3
根据我的理解,OP想要改写历史并利用新更新的.gitignore来过滤掉某些文件。我认为这就是问题所在。 - TTT
2个回答

2
有没有一种方法可以将源分支中的提交回放到目标分支中,但考虑到目标分支中 .gitignore 的更改?
是的,这是可能的,但需要进行一些脚本编写。 (我的示例脚本在下面的循环中甚至可能适用于您。)基本上,您将模拟rebase,但在每个提交之间进行几个步骤。 算法大致如下:
1. 获取要重写的提交列表并将其存储在数组或列表中。 2. 将源分支重置为目标分支。 (现在应该已经放置了.gitignore文件。) 3. 对于列表中的每个提交:使用--no-commit标志cherry-pick提交,以便您不完成提交,然后使用reset取消暂存更改,然后使用add将它们添加回来,这遵守.gitignore指令。
以下是一个工作示例bash脚本(将其保存为文件并在空目录中运行以进行测试):
#!/bin/bash -v

git init

git branch -m main # rename to main in case that's not your default branch name

echo asdf > asdf.txt && git add . && git commit -m "Add asdf.txt"

git branch test-branch

echo "*.log" > .gitignore && git add . && git commit -m "Add .gitignore"

git switch test-branch

echo abc > abc.txt
echo def > def.log
git add . && git commit -m "Add abc.txt and def.log"

echo ghi > ghi.txt
echo hij > hij.log
git add . && git commit -m "Add ghi.txt and hij.log"

git log --all --graph --name-only

# Get all the commit IDs that would be rebased if we rebased test-branch onto main,
# and store them into an array in reverse order
declare -a commitIDs=(`git log main..test-branch --pretty=format:"%h" --reverse`)

# reset test-branch to main
git reset --hard main

# loop through the commitIDs and cherry-pick each one without committing
for i in "${commitIDs[@]}"
do
   echo "About to cherry-pick commit $i"
   git cherry-pick $i --no-commit # this leaves the commit staged
   git reset # unstage so we can commit with the .gitignore
   git add . # this doesn't add ignored files
   git commit --reuse-message=$i
done

git log --all --graph --name-only

完成后,查看两个git log语句的输出。第一个语句有2个提交,每个提交中都有一个.txt和.log文件。第二个语句使用.gitignore文件从这些提交中删除所有.log文件。
请注意,我在重写时使用了先前的提交消息,因为那通常是你想要的。但我故意以这样的方式命名我的提交,以突出一些情况,例如当你忽略的文件名在提交消息中指定时,你不想这样做。

备忘录:也许可以使用 git-filter-repo 或至少 git filter branch 更轻松地完成此操作。 - TTT

0
不,你需要使用git rm来删除你已经跟踪的文件(或者如果你想要清除所有痕迹git-filter-branch)。下面是来自gitignore手册页的相关部分(请注意最后一句话):

gitignore 文件指定 Git 应忽略的有意义的未跟踪文件。Git 已经跟踪的文件不受影响。


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