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

Git "rebase" but ignoring files in new .gitignore

有没有一种方法可以将源分支的提交重播到目标分支,但考虑到目标分支中 .gitignore 中所做的更改?

场景如下:

假设我从 master 分支出来并开始提交文件,包括 .bin 应该在 .gitignore 中但没有的文件。有什么方法可以让我去掌握,将“*.bin”提交到 .gitignore,然后在重新定基(或其他一些自动操作)时修复我的主题分支?通过修复我的意思是删除任何 .bin 文件的变更集,这些文件现在被忽略了。这意味着 1) 如果提交在 a.txtfoo.bin 上有变化,它应该只提交 a.txt 和 2) 如果提交只在 foo.bin 上有变化,它应该是完全下降

目标是轻松清除仅在合并请求时发现的多个错误。

常规 git rebase 无效。即使在 repo 的(新)线性历史记录中,错误提交的文件仍然提交,gitignore 模式在错误提交之前就存在

不,您需要使用 git rm 来删除您已经跟踪的文件(或者如果您想删除它的所有痕迹 git-filter-branch)。这是 gitignore 手册页中的相关位(注意最后一句话):

A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected

Is there a way to replay commits from a source branch into a destination branch but taking into consideration changes made in .gitignore in the destination branch?

是的,这是可能的,但是,这需要一些脚本编写工作。 (我在下面循环中的示例脚本甚至可以按原样为您工作。)基本上,您将模拟变基,但在每次提交之间有几个步骤。该算法看起来像这样:

  1. 获取要重写的提交列表并将它们存储在数组或列表中。
  2. 将您的源分支重置为目标。 (.gitignore 文件现在应该就位。)
  3. 对于列表中的每个提交:使用 --no-commit 标志精心挑选提交,这样您就不会完成提交,然后 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 文件。

请注意,我在重写时使用了以前的提交消息,因为这通常是您想要的。但是我故意以这样的方式命名我的提交,以突出显示您不想这样做的场景,例如,当您忽略的文件名在提交消息中指定时。